<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="icon" src="/images/favicon.gif" />
<title>circle thing</title>
<style type="text/css">
* { padding: 0; margin: 0; border: 0; font-size: 8pt; font-weight: normal; }
html, body {
height: 100%;
width: 100%;
background: #000;
color: #fff;
font-family: sans-serif;
cursor: none;
}
html { display: table; }
body { display: table-cell; vertical-align: middle; text-align: center; }
a { color: #88f; }
</style>
<script type="text/javascript">
/* figure out how we're running relative to expected 60fps */
function speedcalc (samples) {
var lastframe, spd = [];
return new Promise(resolve => {
requestAnimationFrame(function frametimer (frametime) {
if (lastframe && lastframe != frametime)
spd.push(frametime - lastframe);
lastframe = frametime;
if (spd.length < samples) requestAnimationFrame(frametimer);
else return resolve(spd.reduce((s, c) => s += c) / spd.length);
});
});
}
function normalize (v) {
return v.map((h => c => c / h)(v.reduce((s, c) => s += c * c, 0) ** 0.5));
}
addEventListener("load", async function load (e) {
removeEventListener("load", load, false);
var ele = document.getElementsByTagName("canvas")[0];
var scr = ele.getContext("2d");
/* turn off smooth resizing of huge pixels. we want all the pixelly goodness. */
/* having to do this is silly. browser vendors, stop this shit. */
[ "i", "oI", "msI", "mozI", "webkitI" ].every(p => {
if (scr[p + "mageSmoothingEnabled"])
return scr[p + "mageSmoothingEnabled"] = false;
});
var rsz = document.createElement("canvas");
rsz.height = (""+document.location.href).match(/[?&](lines|height)=([^&]*)/) ? RegExp.lastParen : 240;
rsz.width = (""+document.location.href).match(/[?&]columns=([^&]*)/) ? RegExp.lastParen : 4 * rsz.height / 3;
var speed = (""+document.location.href).match(/[?&]speed=([^&]*)/) ? RegExp.lastParen - 0 : 1;
var filled = (""+document.location.href).match(/[?&]filled/) ? true : false;
var trails = (""+document.location.href).match(/[?&]trails/) ? true : false;
await speedcalc(5);
speed *= 50 / 3 / await speedcalc(15);
console.log("running at", Math.round(speed * 10000) / 100 + "% speed");
speed *= rsz.width / 320;
var ctx = rsz.getContext("2d");
var img = ctx.createImageData(rsz.width, rsz.height);
img.putpx = function putpx (y, x, r, g, b, a) {
if (y < 0 || y > img.height || x < 0 || x > img.width) return;
var p = 4 * (Math.floor(y) * img.width + Math.floor(x));
img.data[p + 0] = r; img.data[p + 1] = g;
img.data[p + 2] = b; img.data[p + 3] = a;
}
img.circle = function circle (cy, cx, r, red, grn, blu, alp) {
const eighth = 0.3826834323650898; // Math.sin(45 * Math.PI / 360)
const diameter = img.height;
for (var y = 0; y < r / eighth; y += 1.0) {
for (var x = (r * r - y * y) ** 0.5; x > y - 1; x--) {
img.putpx(cy + y, cx + x, red, grn, blu, alp);
img.putpx(cy + y, cx - x, red, grn, blu, alp);
img.putpx(cy - y, cx + x, red, grn, blu, alp);
img.putpx(cy - y, cx - x, red, grn, blu, alp);
img.putpx(cy + x, cx + y, red, grn, blu, alp);
img.putpx(cy + x, cx - y, red, grn, blu, alp);
img.putpx(cy - x, cx + y, red, grn, blu, alp);
img.putpx(cy - x, cx - y, red, grn, blu, alp);
if (!filled) break;
}
}
}
circles = [...new Array(
(""+document.location.href).match(/[?&]circles=([^&]*)/) ?
RegExp.lastParen - 0 : 30
)];
for (var i in circles) {
var [ dy, dx ] = normalize([0, 0].map(_ => 2 * Math.random()))
.map(n => n * speed);;
var [ red, grn, blu ] = normalize([0, 0, 0].map(Math.random))
.map(n => 255 * n);
var r = img.width / 40 + img.width * 3 / 40 * Math.random();
circles[i] = {
y: r + Math.random() * (img.height - 2 * r),
x: r + Math.random() * (img.width - 2 * r),
dy: dy, dx: dx, r: r,
red: red, grn: grn, blu: blu
};
}
circles.sort((a, b) => b.r - a.r);
requestAnimationFrame(function draw (e) {
if (!trails) for (var i in circles)
img.circle(circles[i].y, circles[i].x, circles[i].r, 0, 0, 0, 255);
for (var i in circles) {
var circle = circles[i];
circle.y += circle.dy;
circle.x += circle.dx;
if (circle.y < circle.r || circle.y > img.height - circle.r) {
circle.y -= circle.dy;
[ circle.dy, circle.dx ] = normalize([
-Math.sign(circle.dy) * Math.random(),
Math.sign(circle.dx) * Math.random()
]).map(n => n * speed);
circle.y += circle.dy;
}
if (circle.x < circle.r || circle.x > img.width - circle.r) {
circle.x -= circle.dx;
[ circle.dy, circle.dx ] = normalize([
Math.sign(circle.dy) * Math.random(),
-Math.sign(circle.dx) * Math.random()
]).map(n => n * speed);
circle.x += circle.dx
}
img.circle(circle.y, circle.x, circle.r, circle.red,
circle.grn, circle.blu, 255);
}
/* draw to buffer */
ctx.putImageData(img, 0, 0);
/* blit to screen */
scr.drawImage(rsz, 0, 0, ele.width, ele.height);
/* queue next frame */
requestAnimationFrame(draw);
});
});
</script>
</head>
<body>
<canvas height="600px" width="800px">Y U no canvas?</canvas>
</body>
</html>