Randomizing SVG Shapes

Avatar of Chris Coyier
Chris Coyier on (Updated on )

Say we wanted to continuously randomize the radius of a circle. We could kinda fake a random look with just CSS, but let’s go for full-on pseudo-random numbers created in JavaScript.

We’re talking SVG here, so here’s our base circle:

<svg viewBox="0 0 100 100">
  <circle id="circle" cx="50" cy="50" r="50" />
</svg>

A little JavaScript function to generate random integers for us within a range:

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}

To randomize that radius every second, we could do:

var circle = document.querySelector("#circle");
setInterval(function() {
  circle.setAttribute("r", getRandomInt(5, 50));
}, 1000);
Just for kicks, we could have it transition to the new size in the browsers that support it:

circle {
  transition: r 0.2s;
}

See the demo.

All SVG shapes are essentially built from numbers, which means all of them are fair game for randomizing.

Imagine a shape with a random-looking jagged bottom like this:

That’s probably normally a (straight lines), but it’s just about as easy to draw as a , which will make it transitionable (in Chrome). So let’s do that. Here’s what that path might look like:

<path d="M-4,-4 L1004,-4 L1004,100 L940,161 L617,186 L555,122 L306,179 L17,177 L-4,100 L-4,-4 Z"></path>

Some of those points are fixed and the rest of those points will be randomized. Here’s a diagram:

Our job now is to generate those random numbers, then assemble them into a string, then replace that string in the DOM (the d attribute of the <path>).

Remember we already have a function for generating random numbers: getRandomInt(). Here’s a simplified concept:

var a = getRandomInt(1, 100);
var b = getRandomInt(1, 100);
var c = getRandomInt(1, 100);

var newString = `${a} ${b} ${c}`;

The trick is to generate and arrange them in the exact format needed for the path syntax. It’s not hard, it’s just a lot of number generation and such. I’ll dump the whole thing here:

var csstricks = {

  init: function() {
    csstricks.randomizeBackgrounds();
  },

  generateRandomPoints: function(minSpread, maxSpread) {
    let points = {};
    points.a = `${getRandomInt(800, 1000)},${getRandomInt(minSpread, maxSpread)}`;
    points.b = `${getRandomInt(600, 800)},${getRandomInt(minSpread, maxSpread)}`;
    points.c = `${getRandomInt(400, 600)},${getRandomInt(minSpread, maxSpread)}`;
    points.d = `${getRandomInt(200, 400)},${getRandomInt(minSpread, maxSpread)}`;
    points.e = `${getRandomInt(0, 200)},${getRandomInt(minSpread, maxSpread)}`;
    return points;
  },

  randomizeHeader: function() {
    let newPoints = csstricks.generateRandomPoints(120, 190);
    let downFacingPoints = `M-4,-4 L1004,-4 L1004,100 L${newPoints.a} L${newPoints.b} L${newPoints.c} L${newPoints.d} L${newPoints.e} L-4,100 L-4,-4 Z`;
    $("#jagged-top").attr("d", downFacingPoints);
  },

  randomizeBackgrounds: function() {
    csstricks.randomizeHeader();
    setInterval(function() {
      csstricks.randomizeHeader();
    }, 2000);
  },

};

csstricks.init();

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}

It’s just broken into smaller functions for organizational purposes. In the final version, I also dropped in a more performant version of setInterval.

Here’s the final demo:

See the Pen
CSS-Tricks Header
by Chris Coyier (@chriscoyier)
on CodePen.

Worth a mention: SVG isn’t really “hardware accelerated” like some web graphics are. For example, if you transition an elements transform or opacity properties, you don’t have to be overly worried about performance as those will be transferred to the computers graphics processor (GPU), as available. No such luck with SVG here in changing out this path data. You might see pretty intense memory and CPU usage in this demo in Chrome because of the CSS transition we have applied. Without the transition (just the instant changing of shape), performance shouldn’t be much of a concern in any browser.

Have fun randomizing!