Using HTML5 canvas to make a generative background

Recently I decided to create a generative background for my webpage. The outcome is a bunch of fluffy clouds that sit in the background of the page, but change each time that you reload the page. It's a fun way to make a background a little bit more interesting.

faded clouds

It's actually quite simple to do, and here are the steps:

Compositing a design on your canvas

You should add a canvas element to the page's html, so that you can actually draw out your generative pattern. Eventually, we'll just use a canvas that is off-page, but when you're starting out you'll definitely want to see what you're working on directly.

<canvas width="300" height="300" id="clouds"></canvas>

Then, proceed to draw onto this canvas like you would normally do. Here is an example function body that could be called once the page is loaded, which randomly rotates each time it's drawn:

  var canvas = document.getElementById("clouds");
  var ctx = canvas.getContext("2d");

  ctx.rotate(Math.random() * 2 * Math.PI);
  ctx.fillStyle = "rgb(200,0,0)";
  ctx.fillRect (10, 10, 55, 50);

  ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
  ctx.fillRect (30, 30, 55, 50);

To actually set this as the background, you can take advantage of the canvas element's toDataURL method which will create an image from your canvas, which you can set as the src of an image or a background:

  document.body.style.background = "url(" + canvas.toDataURL() + ")";

Hiding the canvas

And now once you are happy with your design you can actually hide the original canvas since it's just being drawn onto the background. While you can just hide the element on the page,

<canvas style="display:none" width="1000" height="600" id="clouds"></canvas>

it's better to take advantage of CSS directly. You can get a drawing context directly from the document to draw into, using getCSSCanvasContext, which behaves just like every other canvas drawing context but is off-screen and can be referenced directly through styles. So the above drawing function can become:

  var ctx = document.getCSSCanvasContext("2d", "mybackground", 300, 300);

  ctx.rotate(Math.random() * 2 * Math.PI);
  ctx.fillStyle = "rgb(200,0,0)";
  ctx.fillRect (10, 10, 55, 50);

  ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
  ctx.fillRect (30, 30, 55, 50);

And then this can directly be referenced in a CSS rule:

  body{
    background: -webkit-canvas(mybackground);
  }

"Pinning" the background

It's also possible to have the background stay in fixed position (as a position:fixed element would). This means that when the user scrolls the page the background would stay the same. Do do this, just add the following CSS property to the body:

  background-attachment: fixed;

Changing the opacity of the entire canvas

When I was making my clouds, the clouds I was drawing were initially too bright:

clouds too bright

It's normally easy to just reduce the opacity of things you draw, using something like ctx.globalAlpha. However, when I adjusted this, my clouds got wonky since there was a lot of overlap.

wonky cloud

What I ended up doing was to composite my image onto a canvas element, then draw that image into the css context, which is what ends up being displayed as the background. Here is the general idea:

process for drawing a muted canvas

And the code that runs it:

  canvas = document.createElement("canvas");
  canvas.width = 300;
  canvas.height = 300;
  ctx = canvas.getContext("2d");

  // ... drawing goes here

  var ctx2 = document.getCSSCanvasContext("2d", "mybackground", 300, 300);
  ctx2.globalAlpha = 0.5;
  ctx2.drawImage(canvas, 0, 0);

Which will copy the whole canvas with lower opacity into the background context.