DEV Community

Andy Fitzsimon
Andy Fitzsimon

Posted on

Generative art with vanilla JS

Random computational artwork doesn't have to be hard or heavy

This article was written with interactive images on my blog.
It might still hold up without the visuals


Why design a single piece when you can design a visual system with countless unique permutations?

In this tutorial I'm going to show you how to make dynamic artwork using very simple vanilla javascript and SVG.

For generative art there are some great frameworks like P5.js,Three.js, all the way up to the mind bending Generative Adversarial Networks like styleGAN and so many more. This space is exploding with digital artists and art directors chasing never seen before aesthetics that blur the lines between human and machine made creations.

This isn't new either. For decades many brands with expansive portfolios have gone down the dynamic brand route with their visual architecture. We're lucky to have a few of them as customers at Outfit.io . Traditionally however, permutations were created by central tooling. Often created on the desktop of an art director in a brand team. A render produced once per-need - manually looked over, then distributed as static assets. Nowadays, we can keep the systems alive by leveraging our web native technology and respond to new input

One of the many benefits in using web formats for design, is that it's very easy to produce artwork that is unique every time. An awesome perk for end users of your visual system.
Plus, unlike choosing a specific tool, you have all the aesthetic potential of the web.

This can make for a far more engaging and dynamic aesthetic on large builds like events or media.

Setting the stage

First, we're going to create something without any javascript at all.
This helps us to creatively set the stage by mocking up at least some of the look and feel you'd like to create.

The benefits of this approach means you discover your answers to the most beautiful of questions:

What must stay the same so that
everything else can change?"

The answer is what frees us up to explore new horizons:

Observe what is constant,
discover what can constantly change

If we get a sense of what we like, we'll also get a sense of what variables we can bend or break.

In this case we're going to use SVG, but you could just as easily use plain ol HTML and div's.
I'm not going to use canvas or even Filters because at the end I want a nice vector PDF poster and not a heavy rasterized asset.

<body>
 <svg
  id="stage" 
  viewBox="0 0 100 100" 
  preserveAspectRatio="xMidYMid slice">
<!-- cool stuff will go here -->
 </svg>
</body>
Enter fullscreen mode Exit fullscreen mode

That preserveAspectRatio="xMidYMid slice" will center-crop our artwork if we display it at non square. Think of it as background-size: cover; background-position: 50% 50%; but for SVG contents within a viewBox

We should probably add some CSS to make sure we're full page too.

body{margin:0; overflow:hidden;}
body > svg{width:100vw;height:100vh}
Enter fullscreen mode Exit fullscreen mode

Now, lets think about what we could create.

A few random-ishly placed circles might do the trick.

Not really that random... I just placed the X and Y axis in increments of 20 and the radius in sizes between 2-8.

<circle cx="20" cy="80" r="4" />
<circle cx="80" cy="40" r="4" />
<circle cx="40" cy="40" r="2" />
<circle cx="40" cy="80" r="2" />
<circle cx="60" cy="60" r="6" />
<circle cx="60" cy="20" r="6" />
<circle cx="20" cy="60" r="8" />
<circle cx="80" cy="20" r="8" />
Enter fullscreen mode Exit fullscreen mode

What must stay the same ?

We're going to apply some CSS and attributes to lock in the look we want. In this case, pink circles with double thick lines and no fill.

circle {
  stroke: DeepPink;
  fill: none;   
  stroke-width:2;
}
Enter fullscreen mode Exit fullscreen mode

Okay that was fun, but we need more of these, and I'd go mad placing circles manually. Time to let the computers take over.

Enter Vanilla Javascript

For this demo, we be messing with the three attributes of each SVG circle element.

  • the circle X position
  • the circle Y position
  • the radius of the circle

I also want the option of changing how many circles we play with.

So to accommodate this, we're going to say bye-bye to the manually crafted circles and hello to Javascript generated ones.

I'm going to leave them in the DOM just incase someone is using noscript. But there's no need to do this if we were generating this server-side.
I also want a removeAll function for easily testing new variations.

// clearing the stage
function removeAll() {document.getElementById("stage").innerHTML = "";}
removeAll();
Enter fullscreen mode Exit fullscreen mode

Now we're going to create entirely new circles on the stage with a function called drawCircles

// calling my state svg
let svg = document.getElementById("stage");

function drawCircles() {
  // how many circles to draw 
  // I started with 8 but now I think 15 is the magic number
  for (i = 0; i < 15; i++) {

    // because circle isnt a html element I need to specify the SVG namespace 
    circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");

    // maximum 14, minimum 2 and in increments of 2 
    circle.setAttribute("r", Math.floor(Math.random() * 7) * 2 + 2) ;

    // minimum 20 maximum 80, incremements of 20 
    // (so either 20 , 40, 60, 80 )
    circle.setAttribute("cx", Math.floor(Math.random() * 4) * 20 + 20);
    circle.setAttribute("cy", Math.floor(Math.random() * 4) * 20 + 20);

    // add these elements to my stage
    svg.appendChild(circle);
  }
}
drawCircles();
Enter fullscreen mode Exit fullscreen mode

Mess with the values, it's super fun to see your design going off-plan. Watching your rules bend and break can give you new ideas.

Testing and regenerating

We're going to make testing easy with a timer that changes permutation every 4 seconds.

var reGenTimer = window.setInterval(function () {
  removeAll();
  drawCircles();
}, 4000);
Enter fullscreen mode Exit fullscreen mode

Actually, I'm super impatient so we're also going to add a click event listener so that I can just click for a new generation.

document.addEventListener("click", function () {
  removeAll();
  drawCircles();
});
Enter fullscreen mode Exit fullscreen mode

CSS for some more flair

What's awesome about generating random elements is that you already have them in a random sequence. So you can add very sequential CSS rules using the :nth selectors and still get away with looking random - no need to put this stuff in your JS.

Below, I just make every 2nd, 3rd, 4th and 5th circle a different stroke color.

circle:nth-child(2n){stroke:#6ac}
circle:nth-child(3n){stroke:#678}
circle:nth-child(4n){stroke:#345}
circle:nth-child(5n){stroke:#9ab}
Enter fullscreen mode Exit fullscreen mode

Click below to refresh faster

Play with it on Codepen

And there you have it, we've made generative artwork.
Random but within constraints. Every iteration looks different but also like they belong together. The rules you set – and how wild you go, is entirely up to your vision.

The whole dance of this technique is to ensure every permutation is a hit and looks like something you'd be happy with. That's easier said than done when you can't push pixels, but trust me it's super rewarding knowing you have created a living system rather than a static piece.

What about that poster?

Remember I said we were going to make this a nice vector PDF poster?

Thanks to Make.cm we can, and every single download will be generated uniquely.

Checkout the GET request below for an A3 version
https://api.make.cm/make/t/ad146027-af14-4644-92e8-b13f1209bfda/k/17915c24-7159-4b2a-9552-a50f296fa3d6.c0e9241f95e4c5bbbf17b447538274b7/sync?size=A3&format=pdf&data[blank]=nodata

Top comments (0)