438

A Bird in Flight

Exact generative mathematical implementation of Hamid Naderi Yeganeh's iconic 2016 masterpiece 'A Bird in Flight', formed by 20,001 circles defined by (x - A(k))² + (y - B(k))² = (R(k))² for k ∈ [-10000, 10000]. Features dynamic aerodynamic flight kinematics, progressive compass drafting, interactive banking physics, and archival museum paper palettes.

Playground
60 FPS • Canvas 2D
Click + Drag to interact with field
</>

Full Executable Algorithm Code


import type { ArtRenderer, ParameterState, RenderContext, TimeState } from '../../types/engine';

/**
 * A Bird in Flight by Hamid Naderi Yeganeh (2016)
 * 
 * An exact mathematical implementation of Hamid Naderi Yeganeh's iconic artwork
 * consisting of 20,001 circles of the form:
 *   (x - A(k))^2 + (y - B(k))^2 = (R(k))^2
 * for k in [-10000, 10000].
 * 
 * Augmented with aerodynamic wing kinematics, interactive banking physics,
 * progressive compass drafting, and archival fine-art color palettes.
 */
export function createBirdInFlight(): ArtRenderer {
  const TOTAL_CIRCLES = 20001;

  // Precomputed canonical geometry buffers
  const baseA = new Float32Array(TOTAL_CIRCLES);
  const baseB = new Float32Array(TOTAL_CIRCLES);
  const baseR = new Float32Array(TOTAL_CIRCLES);
  const wingWeight = new Float32Array(TOTAL_CIRCLES);
  const wingPhaseOffset = new Float32Array(TOTAL_CIRCLES);

  // Geometric center and bounds
  const CENTER_Y = -0.7785;
  const SPAN_X = 5.04;
  const SPAN_Y = 2.59;

  let isInitialized = false;

  // Interactive pointer state
  let targetTilt = 0;
  let currTilt = 0;
  let targetOffsetY = 0;
  let currOffsetY = 0;

  function initGeometry() {
    if (isInitialized) return;
    const pi = Math.PI;

    for (let i = 0; i < TOTAL_CIRCLES; i++) {
      const k = i - 10000;
      const u = k / 10000;

      // A(k): Lateral X Coordinate
      const termA1 = (3 * k) / 20000;
      const termA2 = Math.sin((pi / 2) * Math.pow(u, 7)) * Math.pow(Math.cos(41 * pi * u), 6);
      const termA3 = 0.25 * Math.pow(Math.cos(41 * pi * u), 16) * Math.pow(Math.cos(0.5 * pi * u), 12) * Math.sin(6 * pi * u);
      const A = termA1 + termA2 + termA3;

      // B(k): Longitudinal Y Coordinate (Cartesian, positive is upward)
      const termB1 = -Math.cos((pi / 2) * Math.pow(u, 7)) *
        (1 + 1.5 * Math.pow(Math.cos(0.5 * pi * u), 6) * Math.pow(Math.cos(1.5 * pi * u), 6)) *
        Math.pow(Math.cos(41 * pi * u), 6);
      const termB2 = 0.5 * Math.pow(Math.cos(0.3 * pi * u), 10) *
        Math.pow(Math.cos(0.9 * pi * u), 10) *
        Math.pow(Math.cos(1.8 * pi * u), 10);
      const B = termB1 + termB2;

      // R(k): Circle Radius
      const termR1 = 0.02;
      const termR2 = 0.1 * Math.pow(Math.sin(41 * pi * u), 2) * Math.pow(Math.sin(0.9 * pi * u), 2);
      const termR3 = 0.05 * Math.pow(Math.cos(41 * pi * u), 2) * Math.pow(Math.cos(0.5 * pi * u), 10);
      const R = termR1 + termR2 + termR3;

      baseA[i] = A;
      baseB[i] = B;
      baseR[i] = R;

      // Wing factor for aerodynamic flight kinematics (0 along spine/tail/head, 1 at wingtips)
      const absA = Math.abs(A);
      const w = Math.max(0, Math.min(1, (absA - 0.22) / 2.0));
      wingWeight[i] = w;
      wingPhaseOffset[i] = w * 1.35;
    }

    isInitialized = true;
  }

  return {
    setup() {
      initGeometry();
    },

    onPointerMove(x: number, y: number) {
      // Gentle banking and pitch response to pointer
      targetTilt = (x - 0.5) * 0.18;
      targetOffsetY = (y - 0.5) * 0.12;
    },

    render(context: RenderContext, timeState: TimeState, params: ParameterState) {
      const { ctx, width, height } = context;

      initGeometry();

      // Parameter extraction
      const flightSpeed = Number(params.flightSpeed ?? 1.0);
      const flapAmp = Number(params.flapAmplitude ?? 0.12);
      const circleCount = Math.max(1000, Math.min(TOTAL_CIRCLES, Math.round(Number(params.circleCount ?? TOTAL_CIRCLES))));
      const strokeWidth = Number(params.strokeWidth ?? 0.45);
      const colorScheme = Number(params.colorScheme ?? 0);
      const drawProgress = Math.max(0.01, Math.min(1.0, Number(params.drawProgress ?? 1.0)));
      const isDarkMode = Boolean(params.darkMode ?? false);

      const t = timeState.time * flightSpeed;

      // Smooth pointer banking
      currTilt += (targetTilt - currTilt) * 0.06;
      currOffsetY += (targetOffsetY - currOffsetY) * 0.06;

      // Determine palette styles
      // 0: Archival Noir (authentic paper), 1: Midnight Cyber Cyan, 2: Phoenix Flame, 3: Spectral Iridescent, 4: Obsidian Minimal
      const effectiveDark = isDarkMode || colorScheme === 1 || colorScheme === 2 || colorScheme === 3 || colorScheme === 4;

      if (effectiveDark) {
        ctx.fillStyle = colorScheme === 1 ? '#040711' : colorScheme === 2 ? '#0d0706' : '#08090d';
        ctx.fillRect(0, 0, width, height);

        // Subtle dark space ambient glow
        const radGlow = ctx.createRadialGradient(width * 0.5, height * 0.48, 10, width * 0.5, height * 0.48, width * 0.6);
        radGlow.addColorStop(0, colorScheme === 1 ? 'rgba(0, 180, 255, 0.06)' : colorScheme === 2 ? 'rgba(255, 100, 30, 0.07)' : 'rgba(255, 255, 255, 0.03)');
        radGlow.addColorStop(1, 'rgba(0, 0, 0, 0)');
        ctx.fillStyle = radGlow;
        ctx.fillRect(0, 0, width, height);
      } else {
        // Warm archival fine-art museum paper with delicate edge tint
        ctx.fillStyle = '#f8f6f0';
        ctx.fillRect(0, 0, width, height);

        // Archival vignette
        const paperGlow = ctx.createRadialGradient(width * 0.5, height * 0.5, width * 0.2, width * 0.5, height * 0.5, width * 0.7);
        paperGlow.addColorStop(0, 'rgba(255, 255, 255, 0.6)');
        paperGlow.addColorStop(0.85, 'rgba(238, 232, 220, 0.4)');
        paperGlow.addColorStop(1, 'rgba(215, 206, 190, 0.7)');
        ctx.fillStyle = paperGlow;
        ctx.fillRect(0, 0, width, height);
      }

      // Coordinate scaling: fit bird majestically with comfortable breathing margin
      const margin = 0.88;
      const scaleX = (width * margin) / SPAN_X;
      const scaleY = (height * margin) / SPAN_Y;
      const scale = Math.min(scaleX, scaleY);

      const cx = width * 0.5;
      const cy = height * 0.5 + currOffsetY * height * 0.5;

      // Aerodynamic hovering flight oscillation
      const soarRise = Math.sin(t * 1.5) * 0.03;

      ctx.save();
      // Apply banking tilt around bird center
      ctx.translate(cx, cy);
      ctx.rotate(currTilt);
      ctx.translate(-cx, -cy);

      // Total circles to draw based on circleCount and drawProgress
      const step = TOTAL_CIRCLES / circleCount;
      const maxIndex = Math.floor(TOTAL_CIRCLES * drawProgress);

      // Rendering pass: Batch circles by style for optimal 60 FPS performance
      if (colorScheme === 0) {
        // 0: Classic Archival Charcoal Ink
        ctx.strokeStyle = effectiveDark ? 'rgba(240, 242, 248, 0.72)' : 'rgba(18, 20, 26, 0.78)';
        ctx.lineWidth = strokeWidth;

        ctx.beginPath();
        for (let i = 0; i < maxIndex; i += step) {
          const idx = Math.floor(i);
          const A = baseA[idx];
          const B = baseB[idx];
          const R = baseR[idx];
          const w = wingWeight[idx];

          // Dynamic aerodynamic wing flapping
          const flapDisp = flapAmp * Math.pow(w, 1.3) * Math.sin(t * 3.2 - wingPhaseOffset[idx]);
          const pitchDisp = flapDisp * 0.12 * Math.sign(A);

          const mathX = A + pitchDisp;
          const mathY = B + flapDisp + soarRise - CENTER_Y;

          const sx = cx + mathX * scale;
          const sy = cy - mathY * scale;
          const sr = R * scale;

          ctx.moveTo(sx + sr, sy);
          ctx.arc(sx, sy, sr, 0, Math.PI * 2);
        }
        ctx.stroke();

      } else if (colorScheme === 1) {
        // 1: Midnight Cyber Cyan & Electric Violet
        ctx.globalCompositeOperation = 'screen';
        const CHUNK_SIZE = 1200;
        ctx.lineWidth = strokeWidth * 1.1;

        for (let chunkStart = 0; chunkStart < maxIndex; chunkStart += CHUNK_SIZE) {
          const chunkEnd = Math.min(maxIndex, chunkStart + CHUNK_SIZE);
          const progressRatio = chunkStart / TOTAL_CIRCLES;
          
          // Hue transitions from electric cyan (185) at wingtips to deep violet (280) at body
          const hue = 185 + Math.sin(progressRatio * Math.PI) * 95;
          ctx.strokeStyle = `hsla(${hue}, 92%, 62%, 0.65)`;

          ctx.beginPath();
          for (let i = chunkStart; i < chunkEnd; i += step) {
            const idx = Math.floor(i);
            const A = baseA[idx];
            const B = baseB[idx];
            const R = baseR[idx];
            const w = wingWeight[idx];

            const flapDisp = flapAmp * Math.pow(w, 1.3) * Math.sin(t * 3.2 - wingPhaseOffset[idx]);
            const pitchDisp = flapDisp * 0.12 * Math.sign(A);

            const mathX = A + pitchDisp;
            const mathY = B + flapDisp + soarRise - CENTER_Y;

            const sx = cx + mathX * scale;
            const sy = cy - mathY * scale;
            const sr = R * scale;

            ctx.moveTo(sx + sr, sy);
            ctx.arc(sx, sy, sr, 0, Math.PI * 2);
          }
          ctx.stroke();
        }

      } else if (colorScheme === 2) {
        // 2: Phoenix Flame & Radiant Sunlit Embers
        ctx.globalCompositeOperation = 'screen';
        const CHUNK_SIZE = 1200;
        ctx.lineWidth = strokeWidth * 1.1;

        for (let chunkStart = 0; chunkStart < maxIndex; chunkStart += CHUNK_SIZE) {
          const chunkEnd = Math.min(maxIndex, chunkStart + CHUNK_SIZE);
          const progressRatio = chunkStart / TOTAL_CIRCLES;

          // Fiery gold (45) to incandescent crimson (10)
          const hue = 12 + Math.abs(Math.sin(progressRatio * Math.PI + t * 0.4)) * 38;
          ctx.strokeStyle = `hsla(${hue}, 95%, 58%, 0.68)`;

          ctx.beginPath();
          for (let i = chunkStart; i < chunkEnd; i += step) {
            const idx = Math.floor(i);
            const A = baseA[idx];
            const B = baseB[idx];
            const R = baseR[idx];
            const w = wingWeight[idx];

            const flapDisp = flapAmp * Math.pow(w, 1.3) * Math.sin(t * 3.2 - wingPhaseOffset[idx]);
            const pitchDisp = flapDisp * 0.12 * Math.sign(A);

            const mathX = A + pitchDisp;
            const mathY = B + flapDisp + soarRise - CENTER_Y;

            const sx = cx + mathX * scale;
            const sy = cy - mathY * scale;
            const sr = R * scale;

            ctx.moveTo(sx + sr, sy);
            ctx.arc(sx, sy, sr, 0, Math.PI * 2);
          }
          ctx.stroke();
        }

      } else if (colorScheme === 3) {
        // 3: Spectral Iridescent Prismatic Dispersion
        ctx.globalCompositeOperation = effectiveDark ? 'screen' : 'source-over';
        const CHUNK_SIZE = 1000;
        ctx.lineWidth = strokeWidth;

        for (let chunkStart = 0; chunkStart < maxIndex; chunkStart += CHUNK_SIZE) {
          const chunkEnd = Math.min(maxIndex, chunkStart + CHUNK_SIZE);
          const progressRatio = chunkStart / TOTAL_CIRCLES;
          const hue = (progressRatio * 360 + t * 20) % 360;
          ctx.strokeStyle = `hsla(${hue}, 85%, ${effectiveDark ? '65%' : '40%'}, 0.7)`;

          ctx.beginPath();
          for (let i = chunkStart; i < chunkEnd; i += step) {
            const idx = Math.floor(i);
            const A = baseA[idx];
            const B = baseB[idx];
            const R = baseR[idx];
            const w = wingWeight[idx];

            const flapDisp = flapAmp * Math.pow(w, 1.3) * Math.sin(t * 3.2 - wingPhaseOffset[idx]);
            const pitchDisp = flapDisp * 0.12 * Math.sign(A);

            const mathX = A + pitchDisp;
            const mathY = B + flapDisp + soarRise - CENTER_Y;

            const sx = cx + mathX * scale;
            const sy = cy - mathY * scale;
            const sr = R * scale;

            ctx.moveTo(sx + sr, sy);
            ctx.arc(sx, sy, sr, 0, Math.PI * 2);
          }
          ctx.stroke();
        }

      } else {
        // 4: Minimalist Obsidian Noir (Pure Crisp White on Void)
        ctx.strokeStyle = 'rgba(255, 255, 255, 0.75)';
        ctx.lineWidth = strokeWidth;

        ctx.beginPath();
        for (let i = 0; i < maxIndex; i += step) {
          const idx = Math.floor(i);
          const A = baseA[idx];
          const B = baseB[idx];
          const R = baseR[idx];
          const w = wingWeight[idx];

          const flapDisp = flapAmp * Math.pow(w, 1.3) * Math.sin(t * 3.2 - wingPhaseOffset[idx]);
          const pitchDisp = flapDisp * 0.12 * Math.sign(A);

          const mathX = A + pitchDisp;
          const mathY = B + flapDisp + soarRise - CENTER_Y;

          const sx = cx + mathX * scale;
          const sy = cy - mathY * scale;
          const sr = R * scale;

          ctx.moveTo(sx + sr, sy);
          ctx.arc(sx, sy, sr, 0, Math.PI * 2);
        }
        ctx.stroke();
      }

      // Compass cursor indicator when progressively drafting
      if (drawProgress < 0.999 && maxIndex > 0) {
        const lastIdx = Math.min(TOTAL_CIRCLES - 1, maxIndex);
        const lastA = baseA[lastIdx];
        const lastB = baseB[lastIdx];
        const lastR = baseR[lastIdx];
        const lastW = wingWeight[lastIdx];

        const flapDisp = flapAmp * Math.pow(lastW, 1.3) * Math.sin(t * 3.2 - wingPhaseOffset[lastIdx]);
        const pitchDisp = flapDisp * 0.12 * Math.sign(lastA);
        const mathX = lastA + pitchDisp;
        const mathY = lastB + flapDisp + soarRise - CENTER_Y;

        const penX = cx + mathX * scale;
        const penY = cy - mathY * scale;
        const penR = lastR * scale;

        ctx.save();
        ctx.strokeStyle = effectiveDark ? '#00ffff' : '#d97706';
        ctx.lineWidth = 1.5;
        ctx.beginPath();
        ctx.arc(penX, penY, penR + 2, 0, Math.PI * 2);
        ctx.stroke();

        ctx.fillStyle = effectiveDark ? '#38bdf8' : '#b45309';
        ctx.beginPath();
        ctx.arc(penX, penY, 2.5, 0, Math.PI * 2);
        ctx.fill();
        ctx.restore();
      }

      ctx.restore();
    },

    destroy() {
      // Clean up buffers if needed
    }
  };
}
Edit in Interactive Playground Zero Dependencies • Standalone Canvas 2D
ƒ

Mathematical Formulation

high
Analytical Equation
(x−A(k))2+(y−B(k))2=R(k)2,k∈[−10000,10000]A(k)=3k20000+sin⁡(π2(k10000)7)cos⁡6(41πk10000)+14cos⁡16(41πk10000)cos⁡12(πk20000)sin⁡(6πk10000)B(k)=−cos⁡(π2(k10000)7)(1+32cos⁡6(πk20000)cos⁡6(3πk20000))cos⁡6(41πk10000)+12cos⁡10(3πk100000)cos⁡10(9πk100000)cos⁡10(18πk100000)R(k)=150+110sin⁡2(41πk10000)sin⁡2(9πk100000)+120cos⁡2(41πk10000)cos⁡10(πk20000)\begin{aligned} (x - A(k))^2 + (y - B(k))^2 &= R(k)^2, \quad k \in [-10000, 10000] \\ A(k) &= \frac{3k}{20000} + \sin\left(\frac{\pi}{2}\left(\frac{k}{10000}\right)^7\right)\cos^6\left(\frac{41\pi k}{10000}\right) + \frac{1}{4}\cos^{16}\left(\frac{41\pi k}{10000}\right)\cos^{12}\left(\frac{\pi k}{20000}\right)\sin\left(\frac{6\pi k}{10000}\right) \\ B(k) &= -\cos\left(\frac{\pi}{2}\left(\frac{k}{10000}\right)^7\right)\left(1 + \frac{3}{2}\cos^6\left(\frac{\pi k}{20000}\right)\cos^6\left(\frac{3\pi k}{20000}\right)\right)\cos^6\left(\frac{41\pi k}{10000}\right) + \frac{1}{2}\cos^{10}\left(\frac{3\pi k}{100000}\right)\cos^{10}\left(\frac{9\pi k}{100000}\right)\cos^{10}\left(\frac{18\pi k}{100000}\right) \\ R(k) &= \frac{1}{50} + \frac{1}{10}\sin^2\left(\frac{41\pi k}{10000}\right)\sin^2\left(\frac{9\pi k}{100000}\right) + \frac{1}{20}\cos^2\left(\frac{41\pi k}{10000}\right)\cos^{10}\left(\frac{\pi k}{20000}\right) \end{aligned}
Click to expand
Compact Formula
A(k) = 3k/20000 + sin(0.5πu^7)cos^6(41πu) + 0.25cos^16(41πu)cos^12(0.5πu)sin(6πu), B(k) = -cos(0.5πu^7)(1 + 1.5cos^6(0.5πu)cos^6(1.5πu))cos^6(41πu) + 0.5cos^10(0.3πu)cos^10(0.9πu)cos^10(1.8πu), R(k) = 0.02 + 0.1sin^2(41πu)sin^2(0.9πu) + 0.05cos^2(41πu)cos^10(0.5πu)

Mathematical Tags

#bird #bird-in-flight #hamid-naderi-yeganeh #circles #mathematical-art #parametric #creatures #trigonometry #generative
Author: Hamid Naderi Yeganeh Tribute Target: 60 FPS

Export & Embed: A Bird in Flight

4K PNG Snapshot

High-resolution single frame render

WebM Video (5s Loop)

60 FPS browser-captured stream

Standalone JS Script

Complete executable Canvas 2D algorithm

HTML Iframe Embed

<iframe src="https://art.fazleyrabbi.xyz/embed/bird-in-flight" width="500" height="500" frameborder="0" loading="lazy"></iframe>
ESC
↑↓ Navigate ↵ Select
113 Mathematical Artworks
Made by Fazley Rabbi
☕

Support the Project

Keep mathematical creative coding alive & open source

Or via Direct Payoneer ($0 Fee)
Payoneer Customer ID: $0 Fee Direct
24076084

💡 Payoneer app: Go to Pay → Pay to recipient → Enter ID 24076084.

Thank you for supporting generative mathematical animations! ✨