## DEV Community # Convert images to mosaics in p5.js

p5.js is a fun JavaScript library for drawing on an HTML5 canvas, and it has some of the clearest tutorials I have seen. It gives you functionality for things like image manipulation, drawing lines and shapes, displaying images, working with trigonometry, and more. And it is especially popular for generative art, such as fractals.

In this tutorial, I will show you how to use p5.js to convert an image like this: to a mosaic of dots like this: This tutorial assumes a working knowledge of JavaScript and familiarity with pre-algebra, but prior knowledge of p5.js isn't strictly necessary. You can follow along on this by creating an account on the p5.js online editor and logging in. The finished product can be found here.

## 📝 Making a first canvas

As a basic p5.js program, let's start by making a canvas and drawing a single small dot there. We would do that by taking this code to the p5.js editor:

``````function setup() {
createCanvas(300, 200);
}

function draw() {
ellipse(50, 60, 15, 15);
}
``````

We are starting with basic implementations two of the major functions in a p5.js program: `setup` and `draw`.

The `setup` function runs at the beginnng of a p5.js program, and what we're doing in it is calling createCanvas, a built-in function from p5.js, to create a small HTML5 `<canvas>` element of width 300 and height 200.

The `draw` function runs repeatedly in the JavaScript event loop, and what we're doing is calling `ellipse` to put a circle on the canvas, with a diameter of 15 pixels and its center at point `(50, 60)` of that canvas. Remember at school plotting points on Cartesian coordinate grids in math class? That is the same concept here with drawing on a canvas. In fact, a lot of concepts from math class can be used as tools to make cool art!

Now that we've got our setup and draw functions, press play on the p5.js editor, and you should see something like this: One key difference between the Cartesian grids in math class, and the ones in an HTML5 canvas, though, is that as you can see, point `(50, 60)` is at the top-left of the canvas, not the bottom-left. Unlike in the graphs from math class, the y-axis on an HTML5 canvas goes from top to bottom, not bottom to top. The x-axis, though, still goes left to right.

By the way, since we're only drawing our picture once rather than repeatedly (like if we were making an animated p5.js sketch), it's kind of pointless to call `draw` repeatedly. So let's make it so we're only calling `draw` once.

``````  function setup() {
createCanvas(300, 200);
+   noLoop();
}
``````

By adding a call to noLoop, now after the first time we call `draw`, we don't call `draw` again unless our code calls redraw.

Before we move on to loading an image, one other thing worth noting, circles/ellipses are not the only shape you can draw in p5. You can find code to draw other shapes, like lines, curves, rectangles, and more, in the links at this reference.

We've got our canvas made, but now we need a way of loading the image we're editing.

First, in the p5 editor, left of the `sketch.js` filename, click the right arrow to pop our the "sketch files" panel, click the down triangle on the line that says "sketch files", select "upload file" in the dropdown, and then upload your image.

Now, to use the image, add the following code to the p5.js editor, adding a `preload` function and replacing the `setup` function:

``````let img;

function setup() {
createCanvas(img.width, img.height);
noLoop();
}
``````

The `preload` function runs before `setup` to load any assets needed for our p5.js program. What we're doing in our preload function is calling p5.js's loadImage function to load an image, represented in JavaScript as a p5.Image object, that we can manipulate. We store that Image in the `img` global variable. Note that if you're using an image besides `beach.jpg`, you'll want to change the name of the image you're loading in `loadImage`.

Now, in `setup`, we call `createCanvas` like before, but now we use the `Image` object to load the image. We then retrieve the image's width and height so the canvas we make is now the same size as the image.

Now that we've got the image's width and height, and a canvas made in that size, we're going to switch over to drawing the dots on our mosaic.

## 🐆 Plotting the dots

Circling back to our `draw` function, let's replace that function's entire code with this:

``````function draw() { drawMosaic(5) }

// [TODO] Add code to put the dots on the mosaic!
}
``````

Just like how in programming languages like Go, it's a good idea to have the `main` relatively simple, I like having my `draw` function be just a one-liner that calls the function that does the bulk of the action. We're going to have `drawMosaic` be the central function of this program; it takes in the radius we want each dot to be, and it will be in charge of drawing all our dots.

We want dots all over the picture, so let's break up the image into columns; each column will be about 1.5 times the width of a dot (3 times the radius), and will be filled top to bottom with dots. So we'll need to know:

1. How many columns the image will have
2. With that knowledge, how to draw a column.

Let's start by just displaying a vertical line for each column. We'll get rid of the line later, but for now this is helpful as scaffolding, so if something is off about how we render the dots, such as what size they are, or where the dots are drawn, we can figure out what's being drawn in a given column relative to that column's lines.

``````const columnWidth = (dotRadius) => dotRadius * 3;

// [TODO] Replace the line with a column of dots
line(offsetX, 0, offsetX, height);
}

for (let i = 0; i < numberOfColumns(dotRadius); i++) {
}
}
``````

Here's our functions so far:

• `columnWidth` is a helper function to get the width of a column. We have a column be triple the radius of a dot, so that we give each dot a bit of wiggle room as to where it will be drawn.
• `numberOfColumns` tells us how many columns of dots we can fit in the picture. Which is the width of the image divided by the width of a column.
• `drawColumnDots` will be in charge of adding all the dots to a given column, starting at the x-coordinate `offsetX` we pass in and ending at `offsetX + dotRadius`. For now, as scaffolding, we will just draw a straight vertical line at the left edge of the column.
• `drawMosaic` draws every column; we loop over the number of columns we have, and for each one we create a column that starts at the x-coordinate `i` times the width of a column. For example, if we have a column width of 15, then the sixth column of dots (zero indexed, so i = 5) of the mosaic starts at an `offsetX` of 75 pixels.

Press play on the p5.js editor, and you should see something like this: But we didn't come here to draw some vertical lines, we came here to draw some dots, so let's do that!

``````function drawColumnDots(dotRadius, offsetX) {
// [TODO] Replace the line with a column of dots
line(offsetX, 0, offsetX, height);

let dotDiameter = 2 * dotRadius;
let dotHeightWithPadding = dotDiameter + 2;
let numDotsInColumn = Math.floor(height / dotHeightWithPadding);

for (let i = 0; i < numDotsInColumn; i++) {
let centerX = Math.floor(random(
))

ellipse(centerX, centerY, dotDiameter, dotDiameter);
}
}
``````

Here's what happens:

• First, we declare variables for the diameter of a dot, and the height of each dot, with two pixels of padding so the dots aren't touching each other. We then divide the height of the image by `dotHeightWithPadding` to get the number of dots in the column.
• Then, in the for loop, we will draw all the dots, from the top of the column to the bottom. First, we calculate the coordinates of the pixel at the center of the dot.
• For the x-coordinate, the leftmost position a dot can be is `dotRadius` pixels to the right of the start of the column. And the rightmost column is `dotRadius` pixels to the left of the end of the column. So if a column is 15 pixels wide with a 5-pixel dot radius, we would randomly select an x-coordinate between 5 and 10 pixels to the right of the start of a column.
• For the y-coordinate, each dot is `dotHeightWithPadding` pixels lower than the dot above it. We place the top dot's center at `dotRadius` pixels below the top of the pixel, so that the top dots don't get cut off. Looks good, but we could use some randomness vertically too to so the dots aren't necessarily at the same height as the ones to the left and right of each other.

``````+ let topY = Math.floor(random(10));

for (let i = 0; i < numDotsInColumn; i++) {
let centerX = Math.floor(random(
))

ellipse(centerX, centerY, dotDiameter, dotDiameter);
}
`````` Looks good! Before we go on to fill in the colors of the columns, remove the call to `line`, since we no longer need that piece of scaffolding.

## 🎨 Giving the dots their colors

The last step of drawing our mosaic is to color the dots. Each dot will be the same color as the color of the pixel at the center of the dot. Here's how we would do that:

``````  let dotColor = img.get(centerX, centerY);
noStroke()
fill(dotColor);

ellipse(centerX, centerY, dotDiameter, dotDiameter);
``````

Here's what happens:

• First, we use `Image.get` to retrieve the color of the pixel at the coordinates `(centerX, centerY)`. This is represented as an array of 4 numbers: red, green, blue, and alpha-transparency (how see-through a pixel is).
• We call noStroke to remove the outline on the dots, and we call fill to set the color of a dot.
• Finally, calling `ellipse` draws the dot in the color we selected.

Press play on the p5.js editor, and now the canvas will look like this: Cool! One other thing I'd like to add though. This picture has a lot of light-colored pixels, so the dots would stand out better on a dark-colored background. So let's refactor `drawMosaic` so that you can pick the color of the background.

``````function draw() { drawMosaic(10, color(30, 30, 30)); }

We add a new parameter `backgroundColor` to our `drawMosaic` function, and we pass that into background to draw a background. In `draw`, I picked the color `30, 30, 30`; since red/green/blue go from 0 to 255, this gives us a charcoal-black background color. I also made the dot radius 10 pixels instead of 5 to make the picture feel more abstract. Run the play button on the sketch, and now the mosaic looks like this!