Zen Bonsai Tree
A Japanese Moyogi-style Zen Bonsai Tree rendered through Leonardo da Vinci's branching area laws, cubic Bézier trunk splines, flared Nebari roots, and layered Matsu pine needle foliage clouds swaying in a gentle breeze.
60 FPS • Canvas 2D
Click + Drag to interact with field
</>
Full Executable Algorithm Code
590 lines
21007 chars
// 084 - Masterpiece Zen Bonsai Tree (botany)
// 1:1 Original algorithm engine source
function createZenBonsaiTree() {
const PETAL_COUNT = 50;
const petals = [];
function initPetals() {
petals.length = 0;
for (let i = 0; i < PETAL_COUNT; i++) {
petals.push({
x: Math.random(),
y: Math.random(),
vx: 0.0002 + Math.random() * 0.0005,
vy: 0.0004 + Math.random() * 0.0008,
rot: Math.random() * Math.PI * 2,
rotV: (Math.random() - 0.5) * 0.025,
size: 1.5 + Math.random() * 2.5,
phase: Math.random() * Math.PI * 2,
});
}
}
let branchNodes = [];
let foliageClouds = [];
function buildBonsaiModel() {
branchNodes = [];
foliageClouds = [];
// Trunk
branchNodes.push({
start: { x: 0.50, y: 0.81 },
ctrl1: { x: 0.49, y: 0.74 },
ctrl2: { x: 0.44, y: 0.68 },
end: { x: 0.43, y: 0.61 },
thicknessStart: 38,
thicknessEnd: 28,
depth: 0.1,
phase: 0.0,
});
branchNodes.push({
start: { x: 0.43, y: 0.61 },
ctrl1: { x: 0.42, y: 0.54 },
ctrl2: { x: 0.48, y: 0.48 },
end: { x: 0.51, y: 0.43 },
thicknessStart: 28,
thicknessEnd: 20,
depth: 0.1,
phase: 0.4,
});
branchNodes.push({
start: { x: 0.51, y: 0.43 },
ctrl1: { x: 0.54, y: 0.37 },
ctrl2: { x: 0.50, y: 0.31 },
end: { x: 0.47, y: 0.25 },
thicknessStart: 20,
thicknessEnd: 12,
depth: 0.1,
phase: 0.8,
});
branchNodes.push({
start: { x: 0.47, y: 0.25 },
ctrl1: { x: 0.45, y: 0.20 },
ctrl2: { x: 0.49, y: 0.17 },
end: { x: 0.50, y: 0.14 },
thicknessStart: 12,
thicknessEnd: 5,
depth: 0.0,
phase: 1.2,
});
// Sashi-eda Left Wings
branchNodes.push({
start: { x: 0.43, y: 0.60 },
ctrl1: { x: 0.36, y: 0.62 },
ctrl2: { x: 0.28, y: 0.65 },
end: { x: 0.24, y: 0.63 },
thicknessStart: 18,
thicknessEnd: 8,
depth: 0.15,
phase: 0.5,
});
branchNodes.push({
start: { x: 0.33, y: 0.63 },
ctrl1: { x: 0.28, y: 0.58 },
ctrl2: { x: 0.22, y: 0.56 },
end: { x: 0.18, y: 0.55 },
thicknessStart: 9,
thicknessEnd: 4,
depth: 0.2,
phase: 0.8,
});
branchNodes.push({
start: { x: 0.26, y: 0.64 },
ctrl1: { x: 0.22, y: 0.68 },
ctrl2: { x: 0.17, y: 0.69 },
end: { x: 0.14, y: 0.68 },
thicknessStart: 7,
thicknessEnd: 3,
depth: 0.1,
phase: 1.1,
});
// Uke-eda Right Wings
branchNodes.push({
start: { x: 0.50, y: 0.48 },
ctrl1: { x: 0.58, y: 0.49 },
ctrl2: { x: 0.66, y: 0.53 },
end: { x: 0.72, y: 0.51 },
thicknessStart: 16,
thicknessEnd: 7,
depth: 0.2,
phase: 1.3,
});
branchNodes.push({
start: { x: 0.62, y: 0.50 },
ctrl1: { x: 0.68, y: 0.45 },
ctrl2: { x: 0.74, y: 0.43 },
end: { x: 0.80, y: 0.42 },
thicknessStart: 8,
thicknessEnd: 4,
depth: 0.25,
phase: 1.6,
});
branchNodes.push({
start: { x: 0.69, y: 0.52 },
ctrl1: { x: 0.76, y: 0.56 },
ctrl2: { x: 0.82, y: 0.56 },
end: { x: 0.86, y: 0.54 },
thicknessStart: 6,
thicknessEnd: 3,
depth: 0.15,
phase: 1.9,
});
// Ushiro-eda Back Depth
branchNodes.push({
start: { x: 0.47, y: 0.52 },
ctrl1: { x: 0.41, y: 0.46 },
ctrl2: { x: 0.35, y: 0.43 },
end: { x: 0.31, y: 0.42 },
thicknessStart: 12,
thicknessEnd: 5,
depth: 0.7,
phase: 2.2,
});
branchNodes.push({
start: { x: 0.38, y: 0.45 },
ctrl1: { x: 0.34, y: 0.38 },
ctrl2: { x: 0.28, y: 0.36 },
end: { x: 0.24, y: 0.37 },
thicknessStart: 6,
thicknessEnd: 3,
depth: 0.75,
phase: 2.5,
});
// Mid-Upper Tiers
branchNodes.push({
start: { x: 0.52, y: 0.37 },
ctrl1: { x: 0.59, y: 0.34 },
ctrl2: { x: 0.65, y: 0.32 },
end: { x: 0.70, y: 0.30 },
thicknessStart: 11,
thicknessEnd: 5,
depth: 0.3,
phase: 2.8,
});
branchNodes.push({
start: { x: 0.62, y: 0.33 },
ctrl1: { x: 0.68, y: 0.27 },
ctrl2: { x: 0.73, y: 0.25 },
end: { x: 0.77, y: 0.24 },
thicknessStart: 6,
thicknessEnd: 3,
depth: 0.35,
phase: 3.1,
});
branchNodes.push({
start: { x: 0.49, y: 0.32 },
ctrl1: { x: 0.43, y: 0.29 },
ctrl2: { x: 0.38, y: 0.27 },
end: { x: 0.33, y: 0.26 },
thicknessStart: 10,
thicknessEnd: 4,
depth: 0.3,
phase: 3.4,
});
// Apex Crown
branchNodes.push({
start: { x: 0.48, y: 0.22 },
ctrl1: { x: 0.42, y: 0.18 },
ctrl2: { x: 0.39, y: 0.15 },
end: { x: 0.38, y: 0.13 },
thicknessStart: 7,
thicknessEnd: 3,
depth: 0.2,
phase: 3.7,
});
branchNodes.push({
start: { x: 0.49, y: 0.20 },
ctrl1: { x: 0.55, y: 0.17 },
ctrl2: { x: 0.60, y: 0.15 },
end: { x: 0.62, y: 0.14 },
thicknessStart: 7,
thicknessEnd: 3,
depth: 0.2,
phase: 4.0,
});
function generateTufts(count, rx, ry) {
const tufts = [];
for (let i = 0; i < count; i++) {
const u = (Math.random() - 0.5) * 2;
const v = (Math.random() - 0.5) * 1.5;
tufts.push({
dx: u * rx * 0.75,
dy: v * ry * 0.65 - ry * 0.15,
scale: 0.7 + Math.random() * 0.55,
angle: (Math.random() - 0.5) * 0.5,
});
}
return tufts;
}
const cloudDefs = [
{ cx: 0.14, cy: 0.68, rx: 0.085, ry: 0.045, depth: 0.1, density: 1.2, phase: 1.1, count: 12 },
{ cx: 0.18, cy: 0.55, rx: 0.095, ry: 0.050, depth: 0.2, density: 1.3, phase: 0.8, count: 14 },
{ cx: 0.24, cy: 0.63, rx: 0.090, ry: 0.048, depth: 0.15, density: 1.25, phase: 0.5, count: 13 },
{ cx: 0.86, cy: 0.54, rx: 0.080, ry: 0.042, depth: 0.15, density: 1.15, phase: 1.9, count: 11 },
{ cx: 0.80, cy: 0.42, rx: 0.090, ry: 0.048, depth: 0.25, density: 1.3, phase: 1.6, count: 13 },
{ cx: 0.72, cy: 0.51, rx: 0.095, ry: 0.052, depth: 0.2, density: 1.35, phase: 1.3, count: 14 },
{ cx: 0.24, cy: 0.37, rx: 0.080, ry: 0.042, depth: 0.75, density: 1.1, phase: 2.5, count: 10 },
{ cx: 0.31, cy: 0.42, rx: 0.085, ry: 0.045, depth: 0.7, density: 1.15, phase: 2.2, count: 11 },
{ cx: 0.33, cy: 0.26, rx: 0.090, ry: 0.048, depth: 0.3, density: 1.25, phase: 3.4, count: 12 },
{ cx: 0.70, cy: 0.30, rx: 0.085, ry: 0.045, depth: 0.3, density: 1.2, phase: 2.8, count: 12 },
{ cx: 0.77, cy: 0.24, rx: 0.080, ry: 0.042, depth: 0.35, density: 1.15, phase: 3.1, count: 11 },
{ cx: 0.38, cy: 0.13, rx: 0.075, ry: 0.040, depth: 0.2, density: 1.2, phase: 3.7, count: 10 },
{ cx: 0.62, cy: 0.14, rx: 0.075, ry: 0.040, depth: 0.2, density: 1.2, phase: 4.0, count: 10 },
{ cx: 0.50, cy: 0.11, rx: 0.110, ry: 0.055, depth: 0.05, density: 1.45, phase: 1.2, count: 18 },
];
for (const cd of cloudDefs) {
foliageClouds.push({
center: { x: cd.cx, y: cd.cy },
radiusX: cd.rx,
radiusY: cd.ry,
depth: cd.depth,
density: cd.density,
phase: cd.phase,
tufts: generateTufts(cd.count, cd.rx, cd.ry),
});
}
}
return {
setup() {
initPetals();
buildBonsaiModel();
},
render(context, timeState, params) {
const { ctx, width, height } = context;
const speed = Number(params.speed ?? 0.8);
const windSway = Number(params.windSway ?? 1.0);
const foliageDensity = Number(params.foliageDensity ?? 1.3);
const trunkThickness = Number(params.trunkThickness ?? 1.15);
const sunGlow = Number(params.sunGlow ?? 1.1);
const t = timeState.time * speed;
if (petals.length === 0) initPetals();
if (branchNodes.length === 0) buildBonsaiModel();
// Background
const bgGrad = ctx.createRadialGradient(
width * 0.5, height * 0.42, 0,
width * 0.5, height * 0.5, Math.hypot(width, height) * 0.7
);
bgGrad.addColorStop(0, '#19151e');
bgGrad.addColorStop(0.45, '#100e16');
bgGrad.addColorStop(0.85, '#07060a');
bgGrad.addColorStop(1.0, '#030305');
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, width, height);
// Sun Halo
ctx.save();
const sunX = width * 0.52;
const sunY = height * 0.38;
const sunRadius = Math.min(width, height) * 0.32;
const sunAura = ctx.createRadialGradient(sunX, sunY, sunRadius * 0.1, sunX, sunY, sunRadius * 1.6);
sunAura.addColorStop(0, `rgba(220, 60, 30, ${0.42 * sunGlow})`);
sunAura.addColorStop(0.35, `rgba(180, 40, 25, ${0.22 * sunGlow})`);
sunAura.addColorStop(0.7, `rgba(120, 20, 20, ${0.08 * sunGlow})`);
sunAura.addColorStop(1.0, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = sunAura;
ctx.beginPath();
ctx.arc(sunX, sunY, sunRadius * 1.6, 0, Math.PI * 2);
ctx.fill();
const coreGrad = ctx.createRadialGradient(sunX, sunY, 0, sunX, sunY, sunRadius);
coreGrad.addColorStop(0, `rgba(255, 120, 60, ${0.45 * sunGlow})`);
coreGrad.addColorStop(0.65, `rgba(210, 50, 30, ${0.28 * sunGlow})`);
coreGrad.addColorStop(0.95, `rgba(150, 25, 20, ${0.05 * sunGlow})`);
coreGrad.addColorStop(1.0, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = coreGrad;
ctx.beginPath();
ctx.arc(sunX, sunY, sunRadius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// Pot & Moss
const potBaseY = height * 0.88;
const potTopY = height * 0.81;
const potWidth = width * 0.58;
const potLeft = (width - potWidth) * 0.5;
const potRight = potLeft + potWidth;
// Shadow
ctx.save();
const shadowGrad = ctx.createRadialGradient(width * 0.5, potBaseY + 14, 0, width * 0.5, potBaseY + 14, potWidth * 0.65);
shadowGrad.addColorStop(0, 'rgba(0, 0, 0, 0.85)');
shadowGrad.addColorStop(0.5, 'rgba(0, 0, 0, 0.45)');
shadowGrad.addColorStop(1.0, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = shadowGrad;
ctx.beginPath();
ctx.ellipse(width * 0.5, potBaseY + 12, potWidth * 0.56, 16, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// Pot Body
ctx.save();
ctx.beginPath();
ctx.moveTo(potLeft + 16, potTopY);
ctx.lineTo(potRight - 16, potTopY);
ctx.quadraticCurveTo(potRight, potTopY, potRight - 8, potTopY + 14);
ctx.lineTo(potRight - 26, potBaseY);
ctx.quadraticCurveTo(potRight - 32, potBaseY + 6, potRight - 44, potBaseY + 6);
ctx.lineTo(potLeft + 44, potBaseY + 6);
ctx.quadraticCurveTo(potLeft + 32, potBaseY + 6, potLeft + 26, potBaseY);
ctx.lineTo(potLeft + 8, potTopY + 14);
ctx.quadraticCurveTo(potLeft, potTopY, potLeft + 16, potTopY);
ctx.closePath();
const potGrad = ctx.createLinearGradient(potLeft, potTopY, potRight, potBaseY);
potGrad.addColorStop(0, '#221f26');
potGrad.addColorStop(0.25, '#352e3c');
potGrad.addColorStop(0.5, '#443c4d');
potGrad.addColorStop(0.75, '#2c2532');
potGrad.addColorStop(1.0, '#16141a');
ctx.fillStyle = potGrad;
ctx.fill();
ctx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
ctx.lineWidth = 1.2;
ctx.stroke();
ctx.beginPath();
ctx.ellipse(width * 0.5, potTopY + 2, potWidth * 0.51, 9, 0, 0, Math.PI * 2);
ctx.fillStyle = '#3c3543';
ctx.fill();
ctx.strokeStyle = 'rgba(255, 255, 255, 0.25)';
ctx.lineWidth = 1.2;
ctx.stroke();
ctx.fillStyle = '#151318';
ctx.fillRect(potLeft + 36, potBaseY + 6, 20, 9);
ctx.fillRect(potRight - 56, potBaseY + 6, 20, 9);
ctx.restore();
// Moss Mound
ctx.save();
ctx.beginPath();
ctx.ellipse(width * 0.5, potTopY, potWidth * 0.46, 24, 0, Math.PI, 0);
const mossGrad = ctx.createRadialGradient(width * 0.5, potTopY - 6, 12, width * 0.5, potTopY, potWidth * 0.48);
mossGrad.addColorStop(0, '#53823d');
mossGrad.addColorStop(0.35, '#355c27');
mossGrad.addColorStop(0.7, '#1f3816');
mossGrad.addColorStop(1.0, '#101e0a');
ctx.fillStyle = mossGrad;
ctx.fill();
// River Stones
const stones = [
{ x: width * 0.38, y: potTopY - 2, rx: 14, ry: 7, rot: -0.2, c: '#484440' },
{ x: width * 0.62, y: potTopY - 1, rx: 18, ry: 9, rot: 0.15, c: '#363432' },
{ x: width * 0.43, y: potTopY + 3, rx: 11, ry: 5, rot: 0.3, c: '#5a5652' },
];
for (const s of stones) {
ctx.save();
ctx.translate(s.x, s.y);
ctx.rotate(s.rot);
ctx.beginPath();
ctx.ellipse(0, 0, s.rx, s.ry, 0, 0, Math.PI * 2);
ctx.fillStyle = s.c;
ctx.fill();
ctx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
ctx.lineWidth = 0.8;
ctx.stroke();
ctx.restore();
}
ctx.restore();
// Nebari Roots
ctx.save();
const rootBaseX = width * 0.50;
const rootBaseY = potTopY - 8;
const roots = [
{ endDx: -75, endDy: 16, ctrlDx: -45, ctrlDy: -4, w: 16 },
{ endDx: -48, endDy: 14, ctrlDx: -28, ctrlDy: -2, w: 14 },
{ endDx: -22, endDy: 12, ctrlDx: -12, ctrlDy: 0, w: 12 },
{ endDx: 18, endDy: 12, ctrlDx: 10, ctrlDy: 0, w: 12 },
{ endDx: 42, endDy: 14, ctrlDx: 25, ctrlDy: -2, w: 14 },
{ endDx: 70, endDy: 16, ctrlDx: 42, ctrlDy: -4, w: 16 },
{ endDx: -95, endDy: 18, ctrlDx: -65, ctrlDy: 2, w: 10 },
{ endDx: 90, endDy: 18, ctrlDx: 62, ctrlDy: 2, w: 10 },
];
for (const r of roots) {
ctx.beginPath();
ctx.moveTo(rootBaseX + r.ctrlDx * 0.35, rootBaseY - 18);
ctx.quadraticCurveTo(rootBaseX + r.ctrlDx, rootBaseY + r.ctrlDy, rootBaseX + r.endDx, rootBaseY + r.endDy);
ctx.strokeStyle = '#22140d';
ctx.lineWidth = r.w * trunkThickness;
ctx.lineCap = 'round';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(rootBaseX + r.ctrlDx * 0.35 - 2, rootBaseY - 16);
ctx.quadraticCurveTo(rootBaseX + r.ctrlDx - 2, rootBaseY + r.ctrlDy, rootBaseX + r.endDx - 2, rootBaseY + r.endDy);
ctx.strokeStyle = '#5a3b29';
ctx.lineWidth = r.w * 0.38 * trunkThickness;
ctx.stroke();
}
ctx.restore();
// Trunk & Branches
function evalBranchPoint(node, u) {
const u1 = 1 - u;
const sway = Math.sin(t * 1.4 + node.phase) * (4 * u * windSway);
const swayY = Math.cos(t * 1.1 + node.phase) * (2 * u * windSway);
const x = (u1 * u1 * u1 * node.start.x + 3 * u1 * u1 * u * node.ctrl1.x + 3 * u1 * u * u * node.ctrl2.x + u * u * u * node.end.x) * width + sway;
const y = (u1 * u1 * u1 * node.start.y + 3 * u1 * u1 * u * node.ctrl1.y + 3 * u1 * u * u * node.ctrl2.y + u * u * u * node.end.y) * height + swayY;
return { x, y };
}
const sortedBranches = [...branchNodes].sort((a, b) => b.depth - a.depth);
ctx.save();
for (const node of sortedBranches) {
let prevPt = evalBranchPoint(node, 0);
for (let s = 1; s <= 30; s++) {
const u = s / 30;
const curPt = evalBranchPoint(node, u);
const w = (node.thicknessStart * (1 - u) + node.thicknessEnd * u) * trunkThickness;
ctx.beginPath();
ctx.moveTo(prevPt.x, prevPt.y);
ctx.lineTo(curPt.x, curPt.y);
ctx.strokeStyle = node.depth > 0.5 ? '#150d09' : '#22140d';
ctx.lineWidth = w;
ctx.lineCap = 'round';
ctx.stroke();
if (node.depth < 0.5) {
ctx.beginPath();
ctx.moveTo(prevPt.x - w * 0.18, prevPt.y - w * 0.18);
ctx.lineTo(curPt.x - w * 0.18, curPt.y - w * 0.18);
ctx.strokeStyle = '#5a3d2a';
ctx.lineWidth = w * 0.35;
ctx.stroke();
}
prevPt = curPt;
}
}
// Shari deadwood vein
if (branchNodes[1]) {
ctx.beginPath();
for (let s = 0; s <= 25; s++) {
const u = s / 25;
const pt = evalBranchPoint(branchNodes[1], u);
const shariX = pt.x + (Math.sin(s * 0.8) * 3) + 2;
const shariY = pt.y;
if (s === 0) ctx.moveTo(shariX, shariY);
else ctx.lineTo(shariX, shariY);
}
ctx.strokeStyle = 'rgba(215, 205, 190, 0.65)';
ctx.lineWidth = 3.5 * trunkThickness;
ctx.lineCap = 'round';
ctx.stroke();
}
ctx.restore();
// Foliage Clouds
const sortedClouds = [...foliageClouds].sort((a, b) => b.depth - a.depth);
for (const cloud of sortedClouds) {
const sway = Math.sin(t * 1.4 + cloud.phase) * (4 * windSway);
const swayY = Math.cos(t * 1.1 + cloud.phase) * (2 * windSway);
const cx = cloud.center.x * width + sway;
const cy = cloud.center.y * height + swayY;
const rx = cloud.radiusX * width * (0.85 + foliageDensity * 0.15);
const ry = cloud.radiusY * height * (0.85 + foliageDensity * 0.15);
ctx.save();
ctx.beginPath();
ctx.ellipse(cx, cy + ry * 0.15, rx * 1.05, ry * 0.95, 0, 0, Math.PI * 2);
ctx.fillStyle = cloud.depth > 0.5 ? 'rgba(5, 18, 10, 0.95)' : 'rgba(8, 28, 15, 0.95)';
ctx.fill();
for (const tuft of cloud.tufts) {
const tx = cx + tuft.dx * width;
const ty = cy + tuft.dy * height;
const tuftScale = tuft.scale * (0.9 + foliageDensity * 0.1);
const NEEDLES_PER_TUFT = Math.floor(32 * cloud.density * foliageDensity);
for (let n = 0; n < NEEDLES_PER_TUFT; n++) {
const normN = n / NEEDLES_PER_TUFT;
const angle = -Math.PI * 0.25 + normN * (Math.PI * 1.5) + tuft.angle + (Math.sin(n * 5.3) * 0.12);
const rDist = (0.2 + 0.8 * Math.sqrt(Math.random())) * 14 * tuftScale;
const nx0 = tx + Math.cos(angle) * (rDist * 0.4);
const ny0 = ty + Math.sin(angle) * (rDist * 0.4);
const needleLen = (7 + Math.sin(n * 3.7) * 4.5) * tuftScale * (1 - cloud.depth * 0.25);
const nx1 = nx0 + Math.cos(angle) * needleLen;
const ny1 = ny0 + Math.sin(angle) * needleLen;
let needleColor = cloud.depth > 0.5
? (n % 3 === 0 ? '#0f381f' : n % 3 === 1 ? '#164d2b' : '#0a2615')
: (ny1 < cy - ry * 0.15
? (n % 4 === 0 ? '#7fd674' : n % 4 === 1 ? '#4db857' : n % 4 === 2 ? '#a4f092' : '#308c45')
: (n % 3 === 0 ? '#26733a' : n % 3 === 1 ? '#1a542a' : '#123d1e'));
ctx.beginPath();
ctx.moveTo(nx0, ny0);
ctx.lineTo(nx1, ny1);
ctx.strokeStyle = needleColor;
ctx.lineWidth = 1.15;
ctx.stroke();
}
}
ctx.restore();
}
// Petals
ctx.save();
for (let i = 0; i < petals.length; i++) {
const pt = petals[i];
pt.x = (pt.x + pt.vx + 1) % 1;
pt.y = (pt.y + pt.vy + 1) % 1;
pt.rot += pt.rotV;
const px = pt.x * width;
const py = pt.y * height;
const sway = Math.sin(t * 1.8 + pt.phase) * 7;
ctx.save();
ctx.translate(px + sway, py);
ctx.rotate(pt.rot);
ctx.beginPath();
ctx.ellipse(0, 0, pt.size * 1.9, pt.size * 0.95, 0, 0, Math.PI * 2);
ctx.fillStyle = i % 3 === 0 ? 'rgba(255, 185, 200, 0.75)' : i % 3 === 1 ? 'rgba(255, 220, 145, 0.7)' : 'rgba(255, 245, 248, 0.85)';
ctx.fill();
ctx.restore();
}
ctx.restore();
}
};
}
const defaultParams = [
{ key: "windSway", label: "Breeze Sway Amplitude", type: "range", min: 0.2, max: 2.5, step: 0.1, defaultValue: 1.0 },
{ key: "foliageDensity", label: "Pine Needle Cloud Density", type: "range", min: 0.5, max: 2.2, step: 0.1, defaultValue: 1.3 },
{ key: "trunkThickness", label: "Trunk & Limb Caliber", type: "range", min: 0.5, max: 1.8, step: 0.1, defaultValue: 1.15 },
{ key: "sunGlow", label: "Zen Solar Aura Dispersion", type: "range", min: 0.3, max: 2.0, step: 0.1, defaultValue: 1.1 },
{ key: "speed", label: "Breeze Cadence", type: "range", min: 0.2, max: 2.5, step: 0.1, defaultValue: 0.8 }
];
if (!window.__art_instances) window.__art_instances = {};
if (!window.__art_instances['zen-bonsai-tree']) {
const inst = typeof createZenBonsaiTree === 'function' ? createZenBonsaiTree() : null;
if (inst && inst.setup) {
inst.setup({ ctx, width, height, dpr: 1, aspectRatio: width / height }, defaultParams);
}
window.__art_instances['zen-bonsai-tree'] = inst;
}
const instance = window.__art_instances['zen-bonsai-tree'];
if (instance && instance.render) {
instance.render(
{ ctx, width, height, dpr: 1, aspectRatio: width / height },
{ time, deltaTime: dt, frameCount: Math.floor(time * 60), fps: 60 },
defaultParams
);
} Edit in Interactive Playground
Zero Dependencies • Standalone Canvas 2D
ƒ
Mathematical Formulation
high
Analytical Equation
rparentΔ=k=1∑2rkΔ,B(u)=i=0∑3(i3)(1−u)3−iuiPi,Δx(t)=Asin(ωt+ϕ)
Click to expand
∑
Zen Bonsai Tree
Full Mathematical System • botany
100%
Complete System of Equations
[Governing Law][Discrete Progression][Domain & Space][Parameter State]rparentΔ=k=1∑2rkΔ,B(u)=i=0∑3(i3)(1−u)3−iuiPi,Δx(t)=Asin(ωt+ϕ)B(u)=(1−u)3P0+3(1−u)2uP1+3(1−u)u2P2+u3P3,w(u)=w0(1−0.78u)1.4x∈R2,t∈R+,ω∈[0,2π]λwindSway=1(Breeze Sway Amplitude),λfoliageDensity=1.2(Pine Needle Cloud Density),λtrunkThickness=1(Trunk & Limb Caliber),λsunGlow=1.1(Zen Solar Aura Dispersion)
rparentΔ=k=1∑2rkΔ,B(u)=i=0∑3(i3)(1−u)3−iuiPi,Δx(t)=Asin(ωt+ϕ)
Computational Implementation (JavaScript Engine Equivalent)
B(u) = (1-u)^3 P_0 + 3(1-u)^2 u P_1 + 3(1-u) u^2 P_2 + u^3 P_3, \quad w(u) = w_0 (1 - 0.78u)^{1.4} Compact Formula
B(u) = (1-u)^3 P_0 + 3(1-u)^2 u P_1 + 3(1-u) u^2 P_2 + u^3 P_3, \quad w(u) = w_0 (1 - 0.78u)^{1.4} Mathematical Tags
#bonsai
#botany
#tree
#zen
#japanese
#matsuba
#bezier
#leonardo
#fractal
#sakura
Author: Math Art Core Target: 60 FPS
Press ESC or F to exit