Ola everyone !
For a few pens I used a technique I wanted to share. I really like particles and I found interesting to create them from an image.
Deep cat
Dotted map
In this tutorial I will just explain how to generate an array filled
with coordinates from the pixels of our image. Those pens above are
using ThreeJs to create a depth effect but I won't get into 3D over
here. I will just use the Canvas API ;)
What do we need to start ?
You need a basic canvas tag in your HTML:
<canvas id="scene"></canvas>
We will use some CSS to make our result nicer:
body, html{
width:100%;
height:100%;
overflow: hidden;
background: black;
}
canvas{
position: absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
}
And finally you need your image:
Get the infos of your image
Now that we have our basic scene we need to convert our image into an array of data.
Create a new image element and execute the drawScene()
function when it is loaded.
const png = new Image();
png.onload = drawScene;
png.src = "codepen.png";
Now that our image is loaded, we can setup our canvas scene.
const canvas = document.getElementById("scene");
const ctx = canvas.getContext("2d");
canvas.width = png.width;
canvas.height = png.height;
The next step is to draw the image, get the data from the scene and finally clearing it.
To get the data we will use the getImageData
method from the Canvas API.
This method returns an object with an array in it that contains 4 values for each pixel: one for each color (RGB) and a last one for the Alpha.
You can find more info about the getImageData
method here.
ctx.drawImage(png, 0, 0);
const data = ctx.getImageData(0, 0, png.width, png.height);
ctx.clearRect(0,0,canvas.width, canvas.height);
We now have an array with the data of every pixel from the image. But we only want specific pixels. In this case I will select only the pixel with no transparency (but you can target all the blue pixels, the darker pixels [...] It's up to you !).
To select the pixels we need, we will loop through the Y and the X axis of our image. That's why we have a loop into another one.
I check if it's four value (Alpha) is over than 128, the average value. (Each value is between 0 and 255).
If the Alpha is over 128, I push the pixel into my particles
array.
const particles = [];
for (let y = 0, y2 = data.height; y < y2; y++) {
for (let x = 0, x2 = data.width; x < x2; x++) {
if (data.data[(x * 4 + y * 4 * data.width) + 3] > 128) {
const particle = {
x : x,
y : y
};
particles.push(particle);
}
}
}
Check if the array is correct
We can do a quick check by drawing every particle on our scene.
- Set the
fillStyle
to white. - Loop through all the particles.
- Draw each particle with its coordinates.
- And voila !
ctx.fillStyle = "white";
for (let i=0, j=particles.length;i<j;i++) {
const particle = particles[i];
ctx.fillRect(particle.x, particle.y, 1, 1);
}
See below some examples using this technique :)
See the Pen Convert Image to Particles - Static by Louis Hoebregts (@Mamboleoo) on CodePen.
See the Pen Convert Image to Particles - Colorful by Louis Hoebregts (@Mamboleoo) on CodePen.
See the Pen Convert Image to Particles - GSAP by Louis Hoebregts (@Mamboleoo) on CodePen.
Click to rerun
For the demos I had to encode my png into a Base64 image because of cross-domain issue.
I hope you learned something from this article !
Thanks for reading :)
Mamboleoo