We are all addicted to randomness. But in generative art, true chaos is actually the enemy.
As a developer, my first instinct when building anything visual is always Math.random(). Need a particle position? Random. Need a color? Random. It’s quick, it’s messy, and it feels "organic."
But recently, I fell down the rabbit hole of Deterministic Generative Art.
The goal was simple: Could I build a system that takes a string of text (like a name or a date) and outputs a unique, high-fidelity visual that looks exactly the same every single time, without storing the image in a database?
Here is what I learned about moving from chaos to "controlled seeds."
The Problem with Pure Randomness
If you use Math.random(), your art is ephemeral. If a user refreshes the page, their cool design is gone forever. To keep it, you'd have to save a massive PNG to your server. That’s heavy and expensive.
The solution is Seeding.
Instead of asking the browser for a random number, you create a "Pseudo-Random Number Generator" (PRNG). You feed it a "seed" (the user's input), and it gives you a sequence of numbers that look random but are mathematically fixed.
The "Poor Man's" Hash Function
To make this work, you first need to turn a string (e.g., "User123") into a usable number. I started using a simple string-to-hash function. It’s not crypto-secure (don't use this for passwords!), but for art, it’s fast and effective.
function cyrb128(str) {
let h1 = 1779033703, h2 = 3144134277,
h3 = 1013904242, h4 = 2773480762;
for (let i = 0, k; i < str.length; i++) {
k = str.charCodeAt(i);
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
}
// ... bitwise shifts to mix the state ...
return [(h1>>>0) / 4294967296, (h2>>>0) / 4294967296, (h3>>>0) / 4294967296, (h4>>>0) / 4294967296];
}
This little function is the engine. It ingests text and spits out four distinct, float-point numbers between 0 and 1.
Mapping Math to Colors (HSL vs RGB)
Once I had my deterministic numbers, RGB proved too difficult to control. Random RGB values often result in "muddy" browns and grays.
I switched to HSL (Hue, Saturation, Lightness). This changed everything.
- Hue: Mapped to the seed (0 - 360).
- Saturation: Locked between 60% and 100% (to ensure vibrancy).
- Lightness: Modulated by a sine wave based on the cursor position or time.
By constraining the chaos, the results started looking less like "TV static" and more like "Energy Signatures."
Performance at 4K Resolution
The real challenge came when trying to render this on a <canvas> at 4K resolution (3840x2160).
Looping through millions of pixels in JavaScript is a death sentence for the main thread. I had to optimize using OffscreenCanvas and transferring control to a Web Worker. This keeps the UI buttery smooth even while the CPU is crunching complex gradients.
I recently pushed this logic to production in a project called HEDMO, where the algorithm translates birth dates into 4K wallpapers. The interesting part wasn't the UI, but realizing that users feel a deeper connection to the image simply because they know the math behind it is unique to them.
Conclusion
If you are building creative web projects, stop reaching for Math.random().
Build a seed function. Create rules. Let the data drive the art. The result is something that feels magical to the user, but is purely logical to the machine.
What are your favorite libraries for generative art? p5.js? Three.js? Or do you prefer vanilla Canvas API like me? Let me know in the comments.
Top comments (0)