Using Bresenham's Line Drawing Algorithm in JavaScript To Draw A Line Comprsed Entirely Out Of Standard HTML Elements (DIV, that is.)

Still available – Free CSS Book giveaway – $34.99 value, yours free for a retweet.

Do you ever have a project you lay off for later? You know, that one idea you wish you have coded but you can't because you are currently in the middle of a more important thing?

Well, **drawing a line in HTML without canvas** was something on my mind for over a year.

How would something like this work? I created this demo area to show the basic idea in action. Using your mouse **move the line below** to see how - *given any two end points* - we can render a line on a raster display. In this case represented by a simple HTML grid made out of DIV elements!

We'll use Bresenham's line algorithm. It solves the problem of drawing a line in Quadrant 1:

Drawing a line in any direction requires thinking in quadrants. While the algorithm for Quadrant 1 is simple, implementating for all quadrants (and octants) is tricky. However, it's really just a mirror image of the two octants in Quadrant 1.

Many reasons! To provide a clear explanation of how *Bresenham's drawing algorithm* works.

Many *Bresenham's line drawing tutorials* I came across only explained mathematical derivations. But they didn't go into much detail about code implementation *for each octant*. It just felt like implementing something you don't really understand.

It's simple – **some things are just fun.** I mean, wouldn't it be great to draw lines in HTML without using canvas or WebGL? In this line drawing tutorial I will write a JavaScript class that handles instantiation of a single HTML line. You can spawn as many as you like.

I come from game development background. And in games you often deal with drawing lines. But beyond that, I always found the idea of **drawing lines in HTML** fascinating. Probably because I've never seen it done before.

Obviously **raw HTML rendering speed will be an issue**. And yes, further optimizations will be required to achieve meaningful performance. But the focus of this line drawing tutorial is primarily set on the line drawing algorithm explanation itself. You can always re-implement it in any other raster environment or graphics library.

One of the most popular algorithms is perhaps the **Bresenham's line drawing algorithm**. It's pretty simple and straightforward. We take two deltas and calculate the slope of the line equation, based on two ending points of the line.

The *starting* and *ending* points of the line are provided to the four parameters of the draw_line function ([x0, y0] and [x1, y1] respectively).

You can implement this in C, C++, Python, Java or any other language. In this tutorial, of course, we will use JavaScript.

Here is the **pseudo code**:

**draw_line**(x0, y0, x1, y1)
// Calculate "deltas" of the line (difference between two ending points)
dx = x1 - x0
dy = y1 - y0
// Calculate the line equation based on deltas
D = (2 * dy) - dx
y = y0
// Draw the line based on arguments provided
for x from x0 to x1
// Draw pixel at this location
**pixel**(x, y)
// Progress the line drawing algorithm parameters
if D > 0
y = y + 1
D = D - 2*dx
end if
D = D + 2*dy

Converting it to JavaScript, we get the following:

`let `**draw_line** = (x0, y0, x1, y1) => {
// Calculate "deltas" of the line (difference between two ending points)
let dx = x1 - x0;
let dy = y1 - y0;
// Calculate the line equation based on deltas
let D = (2 * dy) - dx;
let y = y0;
// Draw the line based on arguments provided
for (let x = x0; x < x1; x++)
{
// Place pixel on the raster display
**pixel**(x, y);
if (D >= 0)
{
y = y + 1;
D = D - 2 * dx;
}
D = D + 2 * dy;
}
};

First we **calculate the difference between line's end points** on both axis (dx and dy) which are usually referred to as the deltas – for each of the two dimensions respectively.

Then we **calculate the delta of the line as a whole** (here stored in variable D) – you can think of it as the *mathematical equation* for drawing a line. Or in other words the *line slope equation.*

Before iterating through each span of the line let's **reset y-axis counter** (that we will walk using a *for-loop*, as the line is being drawn) by setting it to the initial position on the line. Here y0 refers to the Y coordinate of the first point on the line.

Finally, **draw the line** one "pixel" at a time, in pseudo code shown as **pixel**(x, y) function. Once the pixel is rendered, we can adjust the current pixel position using the delta steps we calculated.

Every time **the delta counter exceeds 0** we step down to the next pixel on the Y-axis and adjust the D variable again by in the opposite dimension, which ends up in progresively drawing the line across both dimensions until we reach line's end.

**But that's not everything.** In addition, depending on ** axis-dominance** the

The barebones **Bresenham's line algorithm** above is designed to draw a line only in one quadrant (Quadrant 1) of the Cartesian coordinate system. But we need to cover all directions. After all, a random line can be plotted from any point on the raster screen to any other point.

This means that in addition to the pseudo code above, we need to take care of two other things:

**Quadrant-Aware Algorithm.**The algorithm must adjust iterator parameters, for each of the four quadrants. We can swap line endpoints to make sure that we always draw the line from left to right, and split the algorithm in two main parts (line is pointing either*up*or*down*from the starting point.) Or we can branch out in four different cases, for each quadrant respectively and just swap x-axis and y-axis iterators to accommodate for line direction.**Axis-Dominance.**But even in each of the**four quadrants**, the line will either be x-axis or y-axis dominant. This means there are actually not four but eight different cases. One for each of the**eight octants**. Luckily, the algorithm stays exactly the same, all we need to do is switch the iterators and branch out a few times.

That's a lot of branching out. But the algorithm presented in the following example will take care of all possible cases.

Okay – all of this sounds great in theory – and we already covered the pseudo code and JavaScript version - but only for one quadrant! Let's take a look at the whole enchilada.

```
let draw_line = (x1, y1, x2, y2) => {
// Iterators, counters required by algorithm
let x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;
// Calculate line deltas
dx = x2 - x1;
dy = y2 - y1;
// Create a positive copy of deltas (makes iterating easier)
dx1 = Math.abs(dx);
dy1 = Math.abs(dy);
// Calculate error intervals for both axis
px = 2 * dy1 - dx1;
py = 2 * dx1 - dy1;
// The line is X-axis dominant
if (dy1 <= dx1) {
// Line is drawn left to right
if (dx >= 0) {
x = x1; y = y1; xe = x2;
} else { // Line is drawn right to left (swap ends)
x = x2; y = y2; xe = x1;
}
pixel(x, y); // Draw first pixel
// Rasterize the line
for (i = 0; x < xe; i++) {
x = x + 1;
// Deal with octants...
if (px < 0) {
px = px + 2 * dy1;
} else {
if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) {
y = y + 1;
} else {
y = y - 1;
}
px = px + 2 * (dy1 - dx1);
}
// Draw pixel from line span at currently rasterized position
pixel(x, y);
}
} else { // The line is Y-axis dominant
// Line is drawn bottom to top
if (dy >= 0) {
x = x1; y = y1; ye = y2;
} else { // Line is drawn top to bottom
x = x2; y = y2; ye = y1;
}
pixel(x, y); // Draw first pixel
// Rasterize the line
for (i = 0; y < ye; i++) {
y = y + 1;
// Deal with octants...
if (py <= 0) {
py = py + 2 * dx1;
} else {
if ((dx < 0 && dy<0) || (dx > 0 && dy > 0)) {
x = x + 1;
} else {
x = x - 1;
}
py = py + 2 * (dx1 - dy1);
}
// Draw pixel from line span at currently rasterized position
pixel(x, y);
}
}
}
```

This is just one way of writing the Bresenham's line drawing algorithm. You can juggle around parameters and improvise on branching out. But the basic idea is there.

The main * if-statement* branches out between two potential

The code is **octant-aware** – within the two if-statement scopes you will also see that the code basically mirrors itself between X and Y axis (tracked by variables **px** and **py**) but the algorithm logic is pretty much identical otherwise.

Often, people ask me how to improve their coding skills. I usually suggest *choosing a project slightly above the range of your current ability*. If you make sure to finish it, this experience will help you advance your coding skills. For me, this was that type of project.