<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="icon" src="/images/favicon.gif" />
<title>fire 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;
}
html { display: table; }
body { display: table-cell; vertical-align: middle; text-align: center; }
a { color: #88f; }
</style>
</head>
<body>
<canvas height="600px" width="800px">Y U no canvas?</canvas>
<script type="text/javascript">
/* let's build a canvas rendering environment comparable to oldschool vga hardware */
/* handily, we also get triple buffering out of this setup, for free. */
var ele = document.getElementsByTagName("canvas")[0];
var scr = ele.getContext("2d");
scr.ratio = ele.width / ele.height;
/* turn off smooth resizing of huge pixels. we want all the pixelly goodness. */
/* having to do this is silly. browser vendors, stop this shit. */
if (scr.imageSmoothingEnabled) scr.imageSmoothingEnabled = false;
else if (scr.webkitImageSmoothingEnabled) scr.webkitImageSmoothingEnabled = false;
else if (scr.mozImageSmoothingEnabled) scr.mozImageSmoothingEnabled = false;
else if (scr.msImageSmoothingEnabled) scr.msImageSmoothingEnabled = false;
else if (scr.oImageSmoothingEnabled) scr.oImageSmoothingEnabled = false;
var rsz = document.createElement("canvas");
rsz.height = (""+document.location.href).match(/[?&](lines|height)=([^&]*)/) ? RegExp.lastParen : 50;
rsz.width = (""+document.location.href).match(/[?&]columns=([^&]*)/) ? RegExp.lastParen : 4 * rsz.height / 3;
var ctx = rsz.getContext("2d");
var img = ctx.createImageData(rsz.width, rsz.height);
/* now, let's build some simple graphics functions */
img.ratio = img.width / img.height;
img.astigmatism = scr.ratio / img.ratio;
img.alpha = 255;
img.color = { red: undefined, grn: undefined, blu: undefined };
img.inBounds = function inBounds () {
return (
this.x >= 0 && this.x < this.width &&
this.y >= 0 && this.y < this.height
);
}
img.setIndex = function setIndex () {
if (!this.inBounds()) return false;
this.i = 4 * (this.width * this.y + this.x);
return this;
}
img.setpxRaw = function setpxRaw () {
var b = this.i;
this.data[b++] = this.color.red & 255;
this.data[b++] = this.color.grn & 255;
this.data[b++] = this.color.blu & 255;
this.data[b] = this.alpha & 255;
return this;
}
img.setpx = function setpx () {
if (!this.setIndex()) return false;
return this.setpxRaw();
}
img.getpxRaw = function getpxRaw () {
var b = this.i;
this.color.red = this.data[b++];
this.color.grn = this.data[b++];
this.color.blu = this.data[b];
/* skip alpha */
return this;
}
img.getpx = function getpx () {
if (!this.setIndex()) return false;
return this.getpxRaw();
}
function palwarn () { console.log("palette fucked"); }
var colors = (""+document.location.href).match(/colors=([^&]*)/) ? RegExp.lastParen : 256;
var pal = (function (c) {
var p = [];
var a = {
red: c.red.x[0] > 0 ? c.red.x.length - 1 : 0,
grn: c.grn.x[0] > 0 ? c.grn.x.length - 1 : 0,
blu: c.blu.x[0] > 0 ? c.blu.x.length - 1 : 0
}, b = {
red: (a.red + 1) % c.red.x.length,
grn: (a.grn + 1) % c.grn.x.length,
blu: (a.blu + 1) % c.blu.x.length
}
for (var i = 0; i < c.len; i++) {
p[i] = {};
var y;
var x = i / c.len;
var dx = {
red: (1 + c.red.x[b.red] - c.red.x[a.red]) % 1,
grn: (1 + c.grn.x[b.grn] - c.grn.x[a.grn]) % 1,
blu: (1 + c.blu.x[b.blu] - c.blu.x[a.blu]) % 1
};
while (x > c.red.x[b.red]) {
b.red = ((a.red = (a.red + 1) % c.red.x.length) + 1) % c.red.x.length;
if (c.red.x[b.red] < c.red.x[a.red]) c.red.x[b.red]++;
}
while (x > c.grn.x[b.grn]) {
b.grn = ((a.grn = (a.grn + 1) % c.grn.x.length) + 1) % c.grn.x.length;
if (c.grn.x[b.grn] < c.grn.x[a.grn]) c.grn.x[b.grn]++;
}
while (x > c.blu.x[b.blu]) {
b.blu = ((a.blu = (a.blu + 1) % c.blu.x.length) + 1) % c.blu.x.length;
if (c.blu.x[b.blu] < c.blu.x[a.blu]) c.blu.x[b.blu]++;
}
x = {
red: x - c.red.x[a.red],
grn: x - c.grn.x[a.grn],
blu: x - c.blu.x[a.blu]
};
if (dx.red) x.red /= dx.red;
else if (c.red.x[a.red] == c.red.x[b.red]) palwarn();
if (dx.grn) x.grn /= dx.grn;
else if (c.grn.x[a.grn] == c.grn.x[b.grn]) palwarn();
if (dx.blu) x.blu /= dx.blu;
else if (c.blu.x[a.blu] == c.blu.x[b.blu]) palwarn();
p[i].red = (1 + Math.cos(Math.PI * x.red)) / 2;
p[i].red *= c.red.y[a.red] - c.red.y[b.red];
p[i].red = Math.ceil((p[i].red + c.red.y[b.red]) * 255);
p[i].grn = (1 + Math.cos(Math.PI * x.grn)) / 2;
p[i].grn *= c.grn.y[a.grn] - c.grn.y[b.grn];
p[i].grn = Math.ceil((p[i].grn + c.grn.y[b.grn]) * 255);
p[i].blu = (1 + Math.cos(Math.PI * x.blu)) / 2;
p[i].blu *= c.blu.y[a.blu] - c.blu.y[b.blu];
p[i].blu = Math.ceil((p[i].blu + c.blu.y[b.blu]) * 255);
}
return p;
}({
len: colors < 256 ? colors : 256,
red: { x: [0/6, 1/6, 6/6], y: [0, 0, 1] },
grn: { x: [0/6, 3/6, 6/6], y: [0, 0, 1] },
blu: { x: [0/6, 4/6, 6/6], y: [0, 0, 1] }
}));
var burnBuffer = new Uint8ClampedArray(img.width * (img.height + 2));
var decay = 2 * colors / img.height;
/* render loop! go! */
var animate = { angle: 0 };
animate.doframe = function () {
/* avoid killing browsers that actually do threads properly */
if (this.busy++) { return --this.busy; }
ctx.putImageData(img, 0, 0);
scr.drawImage(rsz, 0, 0, ele.width, ele.height);
/* burn */
for (img.y = 0; img.y < img.height; img.y++) {
for (img.x = 0; img.x < img.width; img.x++) {
var d = img.width * img.y + img.x;
var s = d + img.width;
var avg = burnBuffer[s];
avg += burnBuffer[s - 1];
avg += burnBuffer[s + img.width];
avg += burnBuffer[s + 1];
avg -= decay;
burnBuffer[d] = avg >> 2;
}
}
for (img.i = img.width * img.height; img.i < (2 + img.height) * img.width; img.i++)
burnBuffer[img.i] = Math.floor(Math.random() * colors);
/* draw to buffer */
for (img.y = 0; img.y < img.height; img.y++) {
for (img.x = 0; img.x < img.width; img.x++) {
img.color = pal[burnBuffer[img.y * img.width + img.x]];
img.setpx();
}
}
/* clear semaphore */
this.busy--;
}
/* kick off the animation */
animate.doframe();
setInterval(animate.doframe.bind(animate), 1000/30);
</script>
</body>
</html>