var W = 300, H = 180;
var c = document.getElementById('c'), ctx = c.getContext('2d');
function paintPhoto(cx, w, h) {
var g = cx.createLinearGradient(0, 0, 0, h * 0.62);
g.addColorStop(0, '#5b86c9'); g.addColorStop(0.55, '#bcd6ea'); g.addColorStop(1, '#f5e0ad');
cx.fillStyle = g; cx.fillRect(0, 0, w, h);
var sx = w * 0.74, sy = h * 0.22, sr = h * 0.11;
var sg = cx.createRadialGradient(sx, sy, 0, sx, sy, sr * 3.2);
sg.addColorStop(0, 'rgba(255,247,214,0.9)'); sg.addColorStop(1, 'rgba(255,247,214,0)');
cx.fillStyle = sg; cx.fillRect(0, 0, w, h * 0.62);
cx.fillStyle = '#fff8df'; cx.beginPath(); cx.arc(sx, sy, sr, 0, 7); cx.fill();
cx.fillStyle = '#6f9152';
cx.beginPath(); cx.moveTo(0, h * 0.64);
for (var x = 0; x <= w; x += 14) cx.lineTo(x, h * 0.64 - Math.sin(x * 0.014) * h * 0.05 - Math.sin(x * 0.032 + 1.2) * h * 0.02);
cx.lineTo(w, h); cx.lineTo(0, h); cx.closePath(); cx.fill();
cx.fillStyle = '#405f38';
cx.beginPath(); cx.moveTo(0, h * 0.79);
for (var x2 = 0; x2 <= w; x2 += 14) cx.lineTo(x2, h * 0.79 - Math.sin(x2 * 0.022 + 0.6) * h * 0.035);
cx.lineTo(w, h); cx.lineTo(0, h); cx.closePath(); cx.fill();
var tx = w * 0.2, ty = h * 0.8;
cx.strokeStyle = '#3a2a1c'; cx.lineWidth = Math.max(1.5, w * 0.008);
cx.beginPath(); cx.moveTo(tx, ty); cx.lineTo(tx - w * 0.01, ty - h * 0.13); cx.stroke();
cx.fillStyle = '#2e4a28';
for (var i = 0; i < 5; i++) { cx.beginPath(); cx.arc(tx + (i - 2) * w * 0.013, ty - h * 0.15 - (i % 2) * h * 0.018, w * 0.028, 0, 7); cx.fill(); }
cx.fillStyle = '#1e1e1c';
cx.beginPath(); cx.arc(w * 0.56, h * 0.745, h * 0.018, 0, 7); cx.fill();
cx.fillRect(w * 0.555, h * 0.76, w * 0.01, h * 0.06);
var img = cx.getImageData(0, 0, w, h), d = img.data;
for (var p = 0; p < d.length; p += 4) { var n = (Math.random() - 0.5) * 10; d[p] += n; d[p + 1] += n; d[p + 2] += n; }
cx.putImageData(img, 0, 0);
}
paintPhoto(ctx, W, H);
var orig = ctx.getImageData(0, 0, W, H);
function radialSample(src, w, h, x, y, scale, channel, cx0, cy0) {
var sx = cx0 + (x - cx0) * scale, sy = cy0 + (y - cy0) * scale;
var xi = Math.round(sx), yi = Math.round(sy);
if (xi < 0) xi = 0; if (xi >= w) xi = w - 1;
if (yi < 0) yi = 0; if (yi >= h) yi = h - 1;
return src[(yi * w + xi) * 4 + channel];
}
function applyCA(src, w, h, amt) {
var out = new Uint8ClampedArray(src.length);
var cx0 = w / 2, cy0 = h / 2;
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
var idx = (y * w + x) * 4;
out[idx] = radialSample(src, w, h, x, y, 1 + amt, 0, cx0, cy0);
out[idx + 1] = src[idx + 1];
out[idx + 2] = radialSample(src, w, h, x, y, 1 - amt, 2, cx0, cy0);
out[idx + 3] = 255;
}
}
return out;
}
var t = 0;
function frame() {
t += 0.018;
var amt = 0.015 + (Math.sin(t) + 1) / 2 * 0.04;
var aberrated = applyCA(orig.data, W, H, amt);
ctx.putImageData(new ImageData(aberrated, W, H), 0, 0);
var frac = (Math.sin(t * 0.5) + 1) / 2 * 0.7 + 0.15;
var sw = Math.round(W * frac);
if (sw > 0) {
var fixed = applyCA(aberrated, W, H, -amt);
ctx.putImageData(new ImageData(fixed, W, H), 0, 0, 0, 0, sw, H);
}
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(sw, 0); ctx.lineTo(sw, H); ctx.stroke();
requestAnimationFrame(frame);
}
frame();