← back to Model Arena
data/artifacts/10c3c876aa67/claude-code.html
223 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fractal Tree — Wind & Falling Leaves</title>
<style>
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; background: #1a2233; }
canvas { display: block; }
#ui {
position: fixed; top: 14px; left: 14px; z-index: 10;
background: rgba(10, 16, 28, 0.55); backdrop-filter: blur(6px);
border: 1px solid rgba(255,255,255,0.15); border-radius: 10px;
padding: 12px 16px; color: #e8e2d0;
font: 13px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
user-select: none;
}
#ui label { display: flex; align-items: center; gap: 10px; }
#depthVal { min-width: 2ch; text-align: center; font-weight: 600; color: #ffb347; }
input[type=range] { width: 160px; accent-color: #ffb347; cursor: pointer; }
#hint { opacity: 0.6; font-size: 11px; margin-top: 4px; }
</style>
</head>
<body>
<div id="ui">
<label>Branch depth
<input type="range" id="depth" min="3" max="11" value="9" step="1">
<span id="depthVal">9</span>
</label>
<div id="hint">tree regrows when depth changes</div>
</div>
<canvas id="c"></canvas>
<script>
(function () {
"use strict";
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
var W = 0, H = 0, DPR = Math.min(window.devicePixelRatio || 1, 2);
function resize() {
W = window.innerWidth; H = window.innerHeight;
canvas.width = W * DPR; canvas.height = H * DPR;
canvas.style.width = W + "px"; canvas.style.height = H + "px";
ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
}
window.addEventListener("resize", resize);
resize();
var maxDepth = 9;
var growStart = performance.now();
var GROW_TIME = 3500; // ms for full growth
var slider = document.getElementById("depth");
var depthVal = document.getElementById("depthVal");
slider.addEventListener("input", function () {
maxDepth = parseInt(slider.value, 10);
depthVal.textContent = maxDepth;
growStart = performance.now(); // regrow from trunk
leaves.length = 0;
ground.length = 0;
});
// deterministic pseudo-random from a seed, so the tree shape is stable per frame
function sr(seed) {
var x = Math.sin(seed * 127.1 + 311.7) * 43758.5453;
return x - Math.floor(x);
}
// autumn leaf palette
var LEAF_COLORS = ["#d9542b", "#e8842a", "#f0b429", "#c23b22", "#e0a41c", "#b8551f"];
// falling leaf particles
var leaves = []; // airborne
var ground = []; // settled on the ground
var MAX_LEAVES = 140, MAX_GROUND = 220;
var tips = []; // leaf-bearing branch tips gathered each frame
function spawnLeaf(x, y, wind) {
if (leaves.length >= MAX_LEAVES) return;
leaves.push({
x: x, y: y,
vx: wind * 12 + (Math.random() - 0.5) * 20,
vy: 8 + Math.random() * 18,
rot: Math.random() * Math.PI * 2,
spin: (Math.random() - 0.5) * 3,
phase: Math.random() * Math.PI * 2,
size: 3 + Math.random() * 3.5,
color: LEAF_COLORS[(Math.random() * LEAF_COLORS.length) | 0]
});
}
function drawLeafShape(x, y, rot, size, color, alpha) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rot);
ctx.globalAlpha = alpha;
ctx.fillStyle = color;
ctx.beginPath();
ctx.ellipse(0, 0, size, size * 0.55, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
ctx.globalAlpha = 1;
}
// recursive branch drawing
function branch(x, y, angle, len, width, depth, seed, growth, t, wind) {
// per-level growth: level d grows during its slice of the growth window
var levelProg = growth * (maxDepth + 1) - depth;
if (levelProg <= 0) return;
if (levelProg > 1) levelProg = 1;
// wind sway: outer, thinner branches sway more; each branch has its own phase
var flexibility = Math.pow((depth + 1) / (maxDepth + 1), 2);
var sway = wind * flexibility * 0.22
+ Math.sin(t * 0.0021 + seed * 6.28 + depth) * 0.02 * flexibility;
var a = angle + sway;
var drawnLen = len * levelProg;
var x2 = x + Math.cos(a) * drawnLen;
var y2 = y + Math.sin(a) * drawnLen;
ctx.strokeStyle = depth < 2 ? "#4a3626" : (depth < maxDepth - 2 ? "#5b442e" : "#6e5638");
ctx.lineWidth = Math.max(width * (0.6 + 0.4 * levelProg), 0.5);
ctx.lineCap = "round";
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x2, y2);
ctx.stroke();
if (depth >= maxDepth || len < 4) {
if (levelProg >= 1) tips.push({ x: x2, y: y2, seed: seed });
return;
}
if (levelProg < 1) return; // children start once this segment finishes
var r1 = sr(seed * 7.3 + depth), r2 = sr(seed * 13.7 + depth * 2), r3 = sr(seed * 29.1);
var spread = 0.38 + r1 * 0.28;
var shrink = 0.68 + r2 * 0.09;
branch(x2, y2, a - spread + (r3 - 0.5) * 0.2, len * shrink, width * 0.7, depth + 1, seed * 2 + 1, growth, t, wind);
branch(x2, y2, a + spread + (r1 - 0.5) * 0.2, len * shrink, width * 0.7, depth + 1, seed * 2 + 2, growth, t, wind);
if (r3 > 0.72 && depth > 1) { // occasional third middle branch
branch(x2, y2, a + (r2 - 0.5) * 0.25, len * shrink * 0.85, width * 0.6, depth + 1, seed * 3 + 5, growth, t, wind);
}
}
function frame(t) {
// sky
var g = ctx.createLinearGradient(0, 0, 0, H);
g.addColorStop(0, "#2b3a5c");
g.addColorStop(0.6, "#5a5a72");
g.addColorStop(1, "#8a6d55");
ctx.fillStyle = g;
ctx.fillRect(0, 0, W, H);
// ground
var groundY = H - 40;
ctx.fillStyle = "#3d2f22";
ctx.fillRect(0, groundY, W, 40);
ctx.fillStyle = "rgba(255, 200, 120, 0.05)";
ctx.fillRect(0, groundY, W, 3);
// gusty wind: layered sines, occasionally strong
var wind = Math.sin(t * 0.0006) * 0.55
+ Math.sin(t * 0.00023 + 1.7) * 0.35
+ Math.sin(t * 0.0017 + 4.1) * 0.18;
var growth = Math.min(1, (t - growStart) / GROW_TIME);
// ease growth
var eased = 1 - Math.pow(1 - growth, 3);
tips.length = 0;
var trunkLen = Math.min(H, 900) * 0.16 + maxDepth * 2;
branch(W / 2, groundY, -Math.PI / 2, trunkLen, Math.max(4, maxDepth * 1.3), 0, 1, eased, t, wind);
// foliage at tips
for (var i = 0; i < tips.length; i++) {
var tip = tips[i];
var c = LEAF_COLORS[(sr(tip.seed) * LEAF_COLORS.length) | 0];
var wob = Math.sin(t * 0.003 + tip.seed * 9) * 1.5 * Math.abs(wind);
drawLeafShape(tip.x + wob, tip.y, sr(tip.seed * 3) * Math.PI * 2 + wind * 0.4, 3.2 + sr(tip.seed * 5) * 2.5, c, 0.9);
// occasionally shed a leaf, more in strong gusts
if (growth >= 1 && Math.random() < 0.0015 + Math.abs(wind) * 0.004) {
spawnLeaf(tip.x, tip.y, wind);
}
}
// update + draw airborne leaves
var dt = 1 / 60;
for (var j = leaves.length - 1; j >= 0; j--) {
var L = leaves[j];
L.phase += dt * 4;
L.vx += (wind * 26 - L.vx) * 0.02 + Math.sin(L.phase) * 6 * dt * 8;
L.vy += (26 - L.vy) * 0.03; // drift toward terminal velocity
L.x += L.vx * dt;
L.y += (L.vy + Math.cos(L.phase * 0.7) * 10) * dt;
L.rot += L.spin * dt + Math.sin(L.phase) * 0.02;
if (L.y >= groundY - 2) {
if (ground.length < MAX_GROUND) {
ground.push({ x: L.x, y: groundY - 1 + Math.random() * 6, rot: L.rot, size: L.size, color: L.color, born: t });
}
leaves.splice(j, 1);
} else if (L.x < -50 || L.x > W + 50) {
leaves.splice(j, 1);
} else {
drawLeafShape(L.x, L.y, L.rot, L.size, L.color, 0.95);
}
}
// settled leaves slowly fade away
for (var k = ground.length - 1; k >= 0; k--) {
var Gl = ground[k];
var age = (t - Gl.born) / 20000;
if (age >= 1) { ground.splice(k, 1); continue; }
drawLeafShape(Gl.x, Gl.y, Gl.rot, Gl.size, Gl.color, 0.8 * (1 - age));
}
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>