TLDR: Demo is here, Code is here, App is here
In Kidz Fun Art, the web app for tablets I’ve built for my kids and hopefully yours, I recently added a nice little feature where you can fill in any area with a linear gradient. It’s highly responsive to the user moving their pen/finger, and can quickly let them change the direction and spacing of the gradient at something like 60 frames per second. This post describes the technical details of how it is achieved.
To see it in action, either try it out yourself at https://kidzfun.art or on the minimal demo page, or watch the video below
Step 1
The user clicks inside some shape that they want to fill with a gradient, in the example below it’s the green diamond.

At this point, the app sends a few things to the Worker Thread:
- An OffscreenCanvas. A Canvas is a 2D drawing element in a web browser. For performance reasons, you can transfer control of a Canvas to a Worker thread, so that as the user is moving their pen around in the single threaded user thread, any paint operations can happen simultaneously without blocking the user’s actions.
- A copy of the pixel data from the user’s Canvas, containing the green diamond you see above
- Some data about the point that the user clicked, and the colours to use in the gradient
Step 2
Now the Worker thread performs a simple flood fill with a solid colour, starting from the point that the user clicked. The resulting pixels are set to black in the OffscreenCanvas (the actual colour doesn’t matter, it just has to be opaque). While doing this we record the bounding box around the pixels for use later.

Step 3
Since we now know the bounding box for the filled in pixels, we can fill it with a linear gradient using the context.createLinearGradient function, as below
const colours = ["#FF0000", "#FFFFFF"];
const gradient = context.createLinearGradient(x1, y1, x2, y2);
colours.forEach((color, index) => {
const stopPosition = index / (colours.length - 1);
gradient.addColorStop(stopPosition, color);
});
context.fillStyle = gradient;
context.fillRect(x1, y1, x2 - x1, y2 - y1);

Normally the code above would fill the entire rectangle with the gradient colours. However we prevent this by using the very cool globalCompositeOperation property on the Canvas context. By setting it to the value “source-in”, any modified pixels are only actually changed if the existing pixel is already non-transparent. So in this case, only the black pixels we previously drew are now drawn with the gradient colours, achieving the goal of rapidly filling any shape at all with a linear gradient.
Step 4
Finally, when the user lifts their pen/finger, the main thread draws the OffscreenCanvas to the main user canvas using the code
userCanvasContext.drawImage(offscreenCanvas, 0, 0);
and the operation is complete: the user canvas now has the selected shape filled with the gradient colours.

Note that you must use the drawImage function to get the image data out of the OffscreenCanvas. You cannot use other operations as you would in a user thread Canvas, such as getting the ImageData from the Canvas context object.
Try it out
I’ve put an example implementation of this online for you to try out.
- The demo is at https://shaneosullivan.github.io/example-gradient-fill/
- The source code is at https://github.com/shaneosullivan/example-gradient-fill
Epilogue
If you’re still here, thanks for following along, and give Kidz Fun Art a try – it’s packed full of goodies, all built using the best that the web and my kids’ imaginations have to offer.