Custom Code Experiment •
Chromatophores 4
Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
overflow: hidden;
background: #000;
padding: 0px;
margin: 0px;
width: 100%;
height: 100%;
}
canvas {
display: block;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var w = canvas.width = window.innerWidth;
var h = canvas.height = window.innerHeight;
window.onresize = function() {
w = canvas.width = window.innerWidth;
h = canvas.height = window.innerHeight;
};
var pixels = [];
var emitterCount = 4;
var maxPixels = 130;
var pixelSize = 18; // base size
var gap = 6;
var lineWidth = 2;
var maxLifetime = 4500;
var minLifetime = 2000;
var spawnRate = 400;
var opacity = 0.22;
var hue = 0;
var hueRange = 60;
var fadeAlpha = 0.018;
var shapes = ["pentagram"]; // only pentagrams
function drawShape(shape, x, y, baseSize, pulse, sizeFactor) {
var s = baseSize * pulse * sizeFactor; // final size
ctx.beginPath();
if (shape === "pentagram") {
var outerRadius = s / 2;
var cx = x;
var cy = y;
var points = [];
// 5 outer points, starting from top (-90°)
for (var i = 0; i < 5; i++) {
var angle = -Math.PI/2 + i * (2 * Math.PI / 5);
points.push({
x: cx + outerRadius * Math.cos(angle),
y: cy + outerRadius * Math.sin(angle)
});
}
// Connect every second point to form a pentagram
var order = [0, 2, 4, 1, 3, 0]; // closes back to start
ctx.moveTo(points[order[0]].x, points[order[0]].y);
for (var j = 1; j < order.length; j++) {
ctx.lineTo(points[order[j]].x, points[order[j]].y);
}
}
}
function emit() {
for (var i = 0; i < emitterCount; i++) {
if (pixels.length >= maxPixels) {
pixels.splice(0, 1);
}
var step = pixelSize + gap;
pixels.push({
x: Math.ceil((Math.random() * w) / step) * step,
y: Math.ceil((Math.random() * h) / step) * step,
t: Math.random() * (maxLifetime - minLifetime) + minLifetime,
s: Date.now(),
h: Math.random() * ((hue + hueRange) - (hue - hueRange)) + (hue - hueRange),
shape: shapes[0], // always "pentagram"
pulseOffset: Math.random() * Math.PI * 2,
pulseSpeed: 2 + Math.random() * 3,
sizeFactor: 1 + Math.random() * 1.5 // range 1.0 to 2.5
});
}
}
function draw() {
var now = Date.now();
for (var i = 0; i < pixels.length; i++) {
var p = pixels[i];
var diff = (now - p.s) / p.t;
var perc = Math.round(diff * 100);
var pulse = 1 + 0.4 * Math.sin(p.pulseOffset + (now / 1000) * p.pulseSpeed);
ctx.save();
ctx.beginPath();
ctx.globalAlpha = opacity;
ctx.lineWidth = lineWidth;
ctx.strokeStyle = "hsl(" + p.h + "," + perc + "%,55%)";
ctx.fillStyle = "hsl(" + p.h + "," + perc + "%,55%)";
drawShape(p.shape, p.x + pixelSize/2, p.y + pixelSize/2, pixelSize, pulse, p.sizeFactor);
ctx.stroke();
ctx.restore();
}
}
function animate() {
var step = pixelSize + gap;
for (var i = 0; i < pixels.length; i++) {
if (pixels[i].s + pixels[i].t < Date.now()) {
pixels.splice(i, 1);
i--;
} else {
var dir = Math.round(Math.random() * 8) % 8;
switch(dir) {
case 0: pixels[i].y -= step; break;
case 1: pixels[i].x += step; pixels[i].y -= step; break;
case 2: pixels[i].x += step; break;
case 3: pixels[i].x += step; pixels[i].y += step; break;
case 4: pixels[i].y += step; break;
case 5: pixels[i].x -= step; pixels[i].y += step; break;
case 6: pixels[i].x -= step; break;
case 7: pixels[i].x -= step; pixels[i].y -= step; break;
}
}
}
}
function render() {
ctx.globalAlpha = fadeAlpha;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, w, h);
ctx.globalAlpha = 1;
draw();
animate();
requestAnimationFrame(render);
}
emit();
setInterval(emit, spawnRate);
requestAnimationFrame(render);
</script>
</body>
</html>