<canvas id="stage" width="512" height="512"></canvas>
html, body {
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
background: rgb(5, 26, 50);
}
View Compiled
// Set up canvas
let canvas = document.querySelector('#stage');
// High DPI Canvas
// https://www.html5rocks.com/en/tutorials/canvas/hidpi/
let devicePixelRatio = window.devicePixelRatio || 1;
let ctx = canvas.getContext('2d');
let backingStoreRatio =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1;
let ratio = devicePixelRatio / backingStoreRatio;
let ow = canvas.width;
let oh = canvas.height;
if (devicePixelRatio !== backingStoreRatio) {
canvas.width = ow * ratio;
canvas.height = oh * ratio;
canvas.style.width = ow + 'px';
canvas.style.height = oh + 'px';
}
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.scale(ratio, ratio);
// initial draw
let {width: w, height: h} = canvas;
ctx.strokeStyle = 'rgba(87,225,255,0.01)';
ctx.fillStyle = 'rgba(87,225,255,0.04)';
ctx.lineWidth = 1;
ctx.lineCap = 'round';
ctx.globalCompositeOperation = 'lighter';
let k = 4; // control points to draw a spline
let N = 10;
let rings = 3;
let controlPtsGroups = [];
for (let o = 0; o < rings; o++) {
// prepare controls point
let r = ow / 6 + o * ow / 8;
let controlPts = [];
let n = N + o * 5;
for (let i = 0; i < n; i++) {
let rad = 2 * Math.PI * i / n;
let x = Math.cos(rad) * r;
let y = Math.sin(rad) * r;
controlPts.push([x, y, x, y]); // [cx, cy, ox, oy]
}
controlPtsGroups.push(controlPts);
}
// The uniform bspline matrix
// http://graphics.cs.ucdavis.edu/~joy/ecs178/Unit-7-Notes/MatrixBSpline.pdf
let UNIFORM_BSPLINE_MATRIX = math.matrix([
[1 / 6, 4 / 6, 1 / 6, 0],
[-3 / 6, 0, 3 / 6, 0],
[3 / 6, -6 / 6, 3 / 6, 0],
[-1 / 6, 3 / 6, -3 / 6, 1 / 6],
]);
let SPLINE_SEGMENT = 20;
// render loop
requestAnimationFrame(function loop() {
for (let o = 0; o < rings; o++) {
let controlPts = controlPtsGroups[o];
let n = controlPts.length;
for (let i = 0; i < n; i += 1) {
let controlMatrix = math.multiply(UNIFORM_BSPLINE_MATRIX, [
controlPts[(i + 0) % n],
controlPts[(i + 1) % n],
controlPts[(i + 2) % n],
controlPts[(i + 3) % n],
]);
for (let j = 0; j < 1; j += 1 / SPLINE_SEGMENT) {
let ts = [1, j, j * j, j * j * j];
let p = math.multiply(ts, controlMatrix);
ctx.fillRect(p.get([0]), p.get([1]), 1, 1);
}
}
// spread
for (let i = 0; i < n; i++) {
let [cx, cy, ox, oy] = controlPts[i];
let spread = 1 + Math.sqrt((cx - ox) * (cx - ox) + (cy - oy) * (cy - oy)) / 20;
controlPts[i][0] += (Math.random() * 2 - 1) * spread;
controlPts[i][1] += (Math.random() * 2 - 1) * spread;
}
}
requestAnimationFrame(loop);
});
View Compiled
This Pen doesn't use any external CSS resources.