<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="icon" src="/images/favicon.gif" />
<title>the latest 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 : 200;
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 pal = (""+document.location.href).match(/colors=([^&]*)/) ? RegExp.lastParen : 256;
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: pal < 256 ? pal : 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 = 600 / img.height;
/* let's add some new stuff to the boring old fire routine */
/* thanks, GPU acceleration! */
var tux = document.createElement("img");
tux.angle = 0;
tux.v = { x: 1, y: 1 };
tux.spin = Math.floor(Math.random() * 2) * 2 - 1;
tux.save = function () {
ctx.translate(tux.p.x, tux.p.y);
ctx.rotate(this.angle / 18);
ctx.translate(-tux.width / 2, -tux.height * 2 / 3);
}
tux.restore = function () {
ctx.translate(tux.width / 2, tux.height * 2 / 3);
ctx.rotate(-this.angle / 18);
ctx.translate(-tux.p.x, -tux.p.y);
}
tux.clear = function () {
tux.save();
ctx.fillStyle = "rgb(0, 0, 0)";
ctx.fillRect(0, 0, tux.width, tux.height);
scr.drawImage(rsz, 0, 0, ele.width, ele.height);
tux.restore();
}
tux.draw = function () {
tux.angle += tux.spin;
tux.save();
ctx.drawImage(tux, 0, 0, tux.width, tux.height);
tux.restore();
}
tux.move = function () {
tux.p.x += tux.v.x;
tux.p.y += tux.v.y;
/* this part could stand some meditation */
if (tux.p.x < tux.height * 2 / 3) {
tux.v.x = 1;
tux.spin = tux.v.y;
} else if (tux.p.y < tux.height * 2 / 3) {
tux.v.y = 1;
tux.spin = -tux.v.x;
} else if (tux.p.x >= (img.width - tux.height * 2 / 3)) {
tux.v.x = -1;
tux.spin = -tux.v.y;
} else if (tux.p.y >= (img.height - tux.height * 2 / 3)) {
tux.v.y = -1;
tux.spin = tux.v.x;
}
}
/* render loop! go! */
var animate = { angle: 0 };
animate.doframe = function () {
/* avoid killing browsers that actually do threads properly */
if (this.busy++) { return --this.busy; }
/* make room for tux */
tux.clear();
/* draw fire */
ctx.putImageData(img, 0, 0);
/* draw tux */
tux.move();
tux.draw();
/* blit to screen */
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() * 256);
/* 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--;
}
/* load the gun */
tux.onload = function () {
/* resize tux for the sake of fitting on the screen */
this.height = this.height * rsz.height / 600;
this.width = this.width * rsz.height / 600;
/* set his initial position */
this.p = {
x: Math.floor(Math.random() * (img.width - this.width)) + this.width / 2,
y: Math.floor(Math.random() * (img.height - this.height)) + this.height / 2
};
/* kick off the animation */
animate.doframe();
setInterval(animate.doframe.bind(animate), 1000/30);
};
/* pull the trigger */
tux.src = "/images/tux-beer.gif";
</script>
</body>
</html>