<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="icon" src="/images/favicon.gif" />
<title>Julia 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>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=800, height=700" />
</head>
<body>
<canvas height="600px" width="800px">Y U no canvas?</canvas>
<div>
<a href="julia.html?columns=80&lines=50&colors=16">mode 03h (text mode)</a> |
<a href="julia.html?columns=320&lines=200&colors=256">mode 13h</a> |
<a href="julia.html?lines=240&colors=256">mode X</a>
</div>
<h1>this is being rendered right now, by your computer</h1>
<script type="text/javascript">
var ele = document.getElementsByTagName("canvas")[0];
var scr = ele.getContext("2d");
scr.ratio = ele.width / ele.height;
// 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|width)=([^&]*)/) ? RegExp.lastParen : 4 * rsz.height / 3;
var ctx = rsz.getContext("2d");
var img = ctx.createImageData(rsz.width, rsz.height);
img.ratio = img.width / img.height;
img.astigmatism = scr.ratio / img.ratio;
img.alpha = 255;
img.putpx = function putpx (c) {
var p = 4 * (this.width * this.y + this.x);
this.data[p + 0] = c.red;
this.data[p + 1] = c.grn;
this.data[p + 2] = c.blu;
this.data[p + 3] = this.alpha;
}
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 < 1530 ? pal : 1530,
red: { x: [0/6, 3/6], y: [0, 1] },
grn: { x: [2/6, 5/6], y: [1, 0] },
blu: { x: [1/6, 4/6], y: [1, 0] }
}));
console.log("animating "+rsz.width+"x"+rsz.height+", "+pal.length+" colors");
function iterate (zr, zi, cr, ci) {
var z2r, z2i, ozr, ozi;
for (
var i = 0, dl = 1, last = -1, ozr = ozi = 100;
i != last; i++
) {
z2r = zr * zr;
z2i = zi * zi;
if (-1 == last && z2r + z2i > 256) break;
zi = 2 * zr * zi + ci;
zr = z2r - z2i + cr;
if (ozr == zr && ozi == zi || i > 128) return -1;
if (i > dl) { ozr = zr; ozi = zi; dl <<= 1; }
}
return i - Math.log(Math.log(z2r + z2i)) / Math.log(2);
}
var animate = {}
animate.doframe = function () {
/* avoid killing browsers that actually do threads properly */
if (this.working++) { return --this.working; }
this.t = (this.t + 1) % 2160;
var i = this.t * Math.PI / 720;
var zr, zi;
if (this.t < 1440) {
/*
zr = -2.15 * Math.cos(i) - Math.cos(2 * i) + .15;
zi = -2.5 * Math.sin(i) - Math.sin(2 * i);
*/
zra = -1.5 * Math.cos(i);
zrb = -1.5 * Math.cos(2 * i);
if (zra > .0125 && zrb < -.025) this.t++;
if (zra > .1 && zrb < -.2) this.t++;
zr = zra + zrb;
zi = -1.5 * Math.sin(i) - 1.5 * Math.sin(2 * i);;
} else {
zr = 1.5 * Math.cos(2 * i) - 4.5;
if (zr < -4.5) this.t++;
zi = -1.5 * Math.sin(2 * i);
}
zr /= 4;
zi /= 4;
ctx.putImageData(img, 0, 0);
scr.drawImage(rsz, 0, 0, ele.width, ele.height);
for (img.y = img.height; img.y--; ) {
var ci = 3 * (img.y - img.height / 2) / img.height;
for (img.x = img.width; img.x--; ) {
var cr = 3 * (img.x - img.width / 2) * img.astigmatism / img.height;
var c = iterate(cr, ci, zr, zi);
c = c == -1 ? { red: 0, grn: 0, blu: 0 } : pal[Math.floor(Math.log(8 + c) * pal.length + pal.length) % pal.length];
img.putpx(c);
}
}
this.working--;
/* draw a dot showing the julia coords
img.x = Math.floor(img.height / 4 * zr + img.width / 2);
img.y = Math.floor(img.height / 4 * zi + img.height / 2);
img.putpx({ red: 255, blu: 255, grn: 255 });
*/
}
animate.t = 0;
animate.doframe();
setInterval(animate.doframe.bind(animate), 1000/30);
</script>
</body>
</html>