Skip to content

Instantly share code, notes, and snippets.

@mattdesl
Created July 12, 2018 01:30
Show Gist options
  • Save mattdesl/d2ab8b8cee617f749a25bad69c0b1182 to your computer and use it in GitHub Desktop.
Save mattdesl/d2ab8b8cee617f749a25bad69c0b1182 to your computer and use it in GitHub Desktop.
paint worklet + generative art (brute force circle packing demo)
<!doctype html>
<div id="sketch"></div>
<style>
body {
margin: 0;
overflow: hidden;
background: #383838;
}
#sketch {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
--seed: 3;
--circle-count: 100;
--circle-size: 1;
--circle-color: #7bf5de;
background-image: paint(canvas-sketch);
}
</style>
<script>
if (location.protocol === 'http:' && location.hostname !== 'localhost')
location.protocol = 'https:';
if ('paintWorklet' in CSS) {
CSS.paintWorklet.addModule('paintworklet.js');
} else {
document.body.innerHTML = 'You need support for <a href="https://drafts.css-houdini.org/css-paint-api/">CSS Paint API</a> to view this demo :(';
}
</script>
// ---- Utilities
// It seems like Math.random() is causing trouble
// with paint worklets ... let's use a seeded one instead
const createRandom = (seed) => {
// From:
// https://gist.github.com/blixt/f17b47c62508be59987b
let _seed = seed % 2147483647;
if (_seed <= 0) _seed += 2147483646;
const next = () => {
_seed = _seed * 16807 % 2147483647;
return _seed;
};
const value = () => {
return (next() - 1) / 2147483646;
};
const gaussian = () => {
return Math.sqrt(-2.0 * Math.log(value())) * Math.cos(2.0 * Math.PI * value())
};
return {
value,
gaussian
};
};
const circleCollides = (a, b, padding = 0) => {
const radii = a.radius + b.radius + padding;
const radiiSq = (radii * radii);
const x = b.position[0] - a.position[0];
const y = b.position[1] - a.position[1];
const distSq = x * x + y * y;
return distSq <= radiiSq;
};
// ---- Sketch / Artwork Code
const sketch = () => {
let seed, random, count;
const circles = [];
const pack = () => {
const darts = 500;
const newCircles = [];
const padding = 0.005;
// throw N darts on board
for (let i = 0; i < darts; i++) {
newCircles.push({
radius: Math.abs(random.gaussian()) * 0.1,
position: [ random.value(), random.value() ],
collisions: 0
});
}
// collide each dart with all other circles
newCircles.forEach(circle => {
circles.forEach(other => {
const collides = circleCollides(circle, other, padding);
if (collides) circle.collisions++;
});
});
// find best fit
newCircles.sort((a, b) => a.collisions - b.collisions)
circles.push(newCircles[0]);
};
const generate = (newSeed = 0, newCount = 100) => {
seed = newSeed;
count = newCount;
random = createRandom(seed);
circles.length = 0;
for (let i = 0; i < count; i++) {
pack();
}
};
generate();
return {
properties: [
'--circle-color',
'--circle-size',
'--circle-count',
'--seed'
],
render ({ context, width, height, css }) {
const color = css.get('--circle-color').toString();
const size = parseFloat(css.get('--circle-size').toString());
const newSeed = parseInt(css.get('--seed').toString(), 10);
const newCount = parseInt(css.get('--circle-count').toString(), 10);
if (newSeed !== seed || newCount !== count) {
generate(newSeed, newCount);
}
context.clearRect(0, 0, width, height);
context.save();
const scale = Math.max(width, height);
context.scale(scale, scale);
circles.forEach(({ position, radius }) => {
context.beginPath();
context.arc(position[0], position[1], radius * size, 0, Math.PI * 2, false);
context.fillStyle = color;
context.fill();
});
context.restore();
}
};
};
// ---- Generic boilerplate stuff, same for each sketch
const run = (props, render) => {
class Sketch {
static get inputProperties () {
return props;
}
paint (context, geometry, css) {
render({
context,
geometry,
width: geometry.width,
height: geometry.height,
css
});
}
}
registerPaint('canvas-sketch', Sketch);
};
let loader = sketch();
if (typeof loader.then !== 'function') {
loader = Promise.resolve(loader);
}
loader.then(result => {
run(result.properties, result.render);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment