ChaotiColor
written by reyrove
In this article, we'll break down the code that makes this magic possible, line by line. you can find the full code right here.
Setting Up the Canvas Ratio
const ratio = 1 / 1
const prefix = 'ChaotiColor'
We start by defining the aspect ratio of the canvas, in this case, a square ratio of 1:1. We also set a prefix for our filename, used later when saving our generated artwork.
Feature Definitions
const features = {}
let resizeTmr = null
let thumbnailTaken = false
Here, we initialize a features
object to store the parameters used for the art generation. We also set up a timer for resizing the canvas (resizeTmr
) and a flag (thumbnailTaken
) to make sure that the preview image is only captured once.
Canvas and Animation Control
const animated = false
let nextFrame = null
Since this generative art doesn’t rely on continuous animation, we set animated
to false. If you wanted to animate the artwork, flipping this value would keep the frames rolling.
Randomized Foreground Colors
const ForegroundColours = [['#9F21DE', '#60DE21'], ['#F20DB2', '#0DF24D'], ...]
A rainbow’s worth of color palettes are defined here. Each color palette contains a mix of hues, chosen to complement and contrast, making sure each creation is vibrant and unique.
Randomized Parameters Setup
features.ForekgroundColour = ForegroundColours[ForegroundIndex]
features.widthdivision = Math.floor($fx.rand() * 300) + 4;
features.heightdivision = Math.floor($fx.rand() * 300) + 4;
We use $fx.rand()
to introduce randomness into the number of horizontal and vertical divisions of the canvas. Each division represents a rectangle, and by tweaking the number of divisions, the artwork looks varied every time.
Cellular Automata Logic
function rules(a, b, c, ruleset) {
if (a == 1 && b == 1 && c == 1) return ruleset[0];
...
if (a == 0 && b == 0 && c == 0) return ruleset[7];
}
This function defines the rules for how the cellular automata evolve across the canvas. It uses a binary ruleset to determine whether a rectangle becomes filled or remains empty based on the values of its neighboring cells.
Binary Vector Generator
function generateBinaryVector(size) {
const vector = new Array(size).fill(0);
vector[Math.floor($fx.rand() * size)] = 1;
for (let i = 1; i < size; i++) {
vector[i] = $fx.rand() < 0.5 ? 0 : 1;
}
return vector;
}
Here, we generate a random binary vector. This vector is key to creating the cellular automata that will paint the artwork. Each value (1 or 0) in the vector represents whether a rectangle is filled with color or left blank.
The Heart of It: The Canvas Drawing Function
const drawCanvas = async () => {
window.cancelAnimationFrame(nextFrame)
const canvas = document.getElementById('target')
const ctx = canvas.getContext('2d')
const w = canvas.width
const h = canvas.height
ctx.fillStyle = 'black'
ctx.fillRect(0, 0, w, h)
let ruleset = generateBinaryVector(8);
let cells = generateBinaryVector(features.widthdivision);
This is where all the magic happens. First, we cancel any existing animation frames to start fresh, and then set up the drawing context. The canvas is painted black, providing the base for the art.
The cellular automata algorithm is applied to the rectangles across the canvas. Each cell, determined by the random binary vector, is either filled with black or with one of the randomized colors from the foreground palette.
Looping Through Rows and Columns
for (let j = 0; j <= A2; j++) {
for (let i = 0; i <= A1 - 1; i++) {
x = i * canvas.width / A1;
y = j * canvas.width / A2;
...
}
}
This loops through the divisions of the canvas, applying the ruleset and painting each rectangle according to the generated binary vector. The magic is in how the colors change based on the evolving automata rules.
Triggering Previews and Downloads
if (!thumbnailTaken) {
$fx.preview()
thumbnailTaken = true
}
Once the canvas is drawn, we capture a preview of the artwork using the $fx.preview()
function, ensuring that it’s only captured once.
if ('forceDownload' in urlParams && forceDownloaded === false) {
forceDownloaded = true;
await autoDownloadCanvas();
}
If a URL parameter exists indicating a forced download, we trigger the autoDownloadCanvas()
function to save the artwork as a PNG file.
Downloading the Artwork
const autoDownloadCanvas = async () => {
const canvas = document.getElementById('target')
const element = document.createElement('a')
const filename = 'forceId' in urlParams
? `${prefix}_${urlParams.forceId.toString().padStart(4, '0')}_${$fx.hash}`
: `${prefix}_${$fx.hash}`
element.setAttribute('download', filename)
const imageBlob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'))
element.setAttribute('href', window.URL.createObjectURL(imageBlob))
element.click()
}
This function creates a download link for the artwork. It automatically names the file using the $fx.hash
, ensuring each piece has a unique identifier.
And there you have it! The ChaotiColor artwork is a wild ride of color, randomness, and cellular automata magic. Each time you run it, you get a completely different creation, brought to life by clever code and a touch of chaos. So, go ahead—play with the code, change the parameters, and create your own ChaotiColor masterpiece.
With boundless love and a dash of digital magic, 💖
_Frostbond Coders