97

Royal Symphysodon Discus

Amazonian disciform fish simulation featuring a circular laterally compressed body profile, vertical melanin Heckel stress bars, labyrinthine electric turquoise striations, and ruby-red eyes.

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

Full Executable Algorithm Code

// 077 - Royal Symphysodon Discus (creatures)
// 1:1 Original algorithm engine source
function createSymphysodonDiscus() {
  return {
    setup() {
    },
    render(context, timeState, params) {
      const { ctx, width, height } = context;
      const hoverSpeed = Number(params.hoverRate ?? 0.8);
      const neonGlow = Number(params.striationLuster ?? 1.1);
      const verticalBars = Math.max(5, Math.min(11, Math.round(Number(params.stressBars ?? 7))));
      const t = timeState.time * hoverSpeed;
      ctx.fillStyle = "#050403";
      ctx.fillRect(0, 0, width, height);
      const cx = width * 0.5;
      const cy = height * 0.5;
      const maxR = Math.min(width, height) * 0.44;
      const hoverY = Math.sin(t * 2.2) * 5;
      ctx.save();
      ctx.translate(cx, cy + hoverY);
      const turquoiseHue = 185;
      const baseAmberHue = 28;
      const finRays = 40;
      for (const fSide of [-1, 1]) {
        for (let r = 0; r < finRays; r++) {
          const rFrac = r / (finRays - 1);
          const rAngle = -Math.PI * 0.6 + rFrac * Math.PI * 1.2;
          const rBaseX = Math.sin(rAngle) * (maxR * 0.52);
          const rBaseY = fSide * (Math.cos(rAngle) * (maxR * 0.52));
          const wave = Math.sin(t * 3.5 - rFrac * 4) * 8;
          const finHeight = maxR * 0.22 * Math.sin(rFrac * Math.PI) * (1 + 0.08 * Math.sin(t * 2 + r));
          const tipX = rBaseX + wave * 0.4;
          const tipY = rBaseY + fSide * finHeight;
          ctx.beginPath();
          ctx.moveTo(rBaseX, rBaseY);
          ctx.lineTo(tipX, tipY);
          ctx.strokeStyle = hsla(turquoiseHue + rFrac * 30, 95, 65, 0.45 * neonGlow);
          ctx.lineWidth = 1.4;
          ctx.stroke();
          if (r % 3 === 0) {
            ctx.fillStyle = hsla(turquoiseHue + 40, 100, 85, 0.7 * neonGlow);
            ctx.beginPath();
            ctx.arc(tipX, tipY, 1.2, 0, Math.PI * 2);
            ctx.fill();
          }
        }
      }
      const tailLen = maxR * 0.35;
      const tailWave = Math.sin(t * 3.5) * 6;
      ctx.beginPath();
      ctx.moveTo(maxR * 0.5, 0);
      ctx.bezierCurveTo(maxR * 0.65, -maxR * 0.2, maxR * 0.85 + tailWave, -maxR * 0.25, maxR * 0.5 + tailLen + tailWave, 0);
      ctx.bezierCurveTo(maxR * 0.85 + tailWave, maxR * 0.25, maxR * 0.65, maxR * 0.2, maxR * 0.5, 0);
      ctx.fillStyle = "rgba(56, 189, 248, 0.2)";
      ctx.fill();
      ctx.strokeStyle = hsla(turquoiseHue, 95, 75, 0.7 * neonGlow);
      ctx.lineWidth = 1.2;
      ctx.stroke();
      ctx.beginPath();
      ctx.ellipse(0, 0, maxR * 0.52, maxR * 0.54, 0, 0, Math.PI * 2);
      const discGrad = ctx.createRadialGradient(0, 0, maxR * 0.1, 0, 0, maxR * 0.54);
      discGrad.addColorStop(0, hsla(baseAmberHue + 15, 95, 52, 0.98));
      discGrad.addColorStop(0.65, hsla(baseAmberHue, 90, 36, 0.95));
      discGrad.addColorStop(1, hsla(turquoiseHue - 20, 85, 25, 0.95));
      ctx.fillStyle = discGrad;
      ctx.fill();
      ctx.strokeStyle = hsla(turquoiseHue, 100, 80, 0.9 * neonGlow);
      ctx.lineWidth = 1.8;
      ctx.stroke();
      for (let b = 1; b <= verticalBars; b++) {
        const bFrac = b / (verticalBars + 1);
        const bx = -maxR * 0.42 + bFrac * (maxR * 0.84);
        const bHalfH = Math.sqrt(Math.max(0, Math.pow(maxR * 0.52, 2) - bx * bx)) * 0.92;
        ctx.beginPath();
        ctx.moveTo(bx, -bHalfH);
        ctx.lineTo(bx, bHalfH);
        const isCenterBar = Math.abs(b - verticalBars / 2) < 1;
        ctx.strokeStyle = isCenterBar ? "rgba(8, 6, 4, 0.75)" : "rgba(15, 10, 6, 0.45)";
        ctx.lineWidth = isCenterBar ? 3.5 : 2;
        ctx.stroke();
      }
      for (let w = 1; w <= 9; w++) {
        const wy = -maxR * 0.38 + w * (maxR * 0.08);
        ctx.beginPath();
        const steps = 30;
        for (let s = 0; s <= steps; s++) {
          const sFrac = s / steps;
          const sx = -maxR * 0.42 + sFrac * (maxR * 0.84);
          const limitH = Math.sqrt(Math.max(0, Math.pow(maxR * 0.5, 2) - sx * sx));
          if (Math.abs(wy) < limitH) {
            const waveY = wy + Math.sin(sx * 0.08 + t * 2 + w) * 3;
            if (s === 0) ctx.moveTo(sx, waveY);
            else ctx.lineTo(sx, waveY);
          }
        }
        ctx.strokeStyle = hsla(turquoiseHue + w * 4, 100, 75, 0.75 * neonGlow);
        ctx.lineWidth = 1.3;
        ctx.stroke();
      }
      ctx.beginPath();
      ctx.ellipse(-maxR * 0.12, maxR * 0.08, maxR * 0.15, maxR * 0.08, 0.5 + Math.sin(t * 4) * 0.2, 0, Math.PI * 2);
      ctx.fillStyle = "rgba(254, 240, 138, 0.25)";
      ctx.fill();
      ctx.strokeStyle = hsla(45, 95, 80, 0.85);
      ctx.lineWidth = 1.1;
      ctx.stroke();
      const eyeX = -maxR * 0.32;
      const eyeY = -maxR * 0.12;
      ctx.beginPath();
      ctx.arc(eyeX, eyeY, 5.5, 0, Math.PI * 2);
      ctx.fillStyle = "#b91c1c";
      ctx.fill();
      ctx.strokeStyle = hsla(45, 100, 75, 0.9);
      ctx.lineWidth = 1.4;
      ctx.stroke();
      ctx.beginPath();
      ctx.arc(eyeX, eyeY, 2.5, 0, Math.PI * 2);
      ctx.fillStyle = "#000000";
      ctx.fill();
      ctx.fillStyle = "#ffffff";
      ctx.beginPath();
      ctx.arc(eyeX - 1.2, eyeY - 1.2, 1.1, 0, Math.PI * 2);
      ctx.fill();
      ctx.restore();
    }
  };
}

// Default parameters from content metadata
const defaultParams = [
  {
    "key": "hoverRate",
    "label": "Hovering Dynamics",
    "type": "range",
    "min": 0.3,
    "max": 1.6,
    "step": 0.05,
    "defaultValue": 0.8,
    "description": "Slow buoyant hovering cycle speed"
  },
  {
    "key": "striationLuster",
    "label": "Neon Cyan Striations",
    "type": "range",
    "min": 0.4,
    "max": 1.8,
    "step": 0.1,
    "defaultValue": 1.1,
    "description": "Electric turquoise striation brightness"
  },
  {
    "key": "stressBars",
    "label": "Vertical Heckel Bars",
    "type": "range",
    "min": 5,
    "max": 11,
    "step": 2,
    "defaultValue": 7,
    "description": "Number of dark vertical melanin stress bars"
  }
];

if (!window.__art_instances) window.__art_instances = {};
if (!window.__art_instances['symphysodon-discus']) {
  const inst = typeof createSymphysodonDiscus === 'function' ? createSymphysodonDiscus() : null;
  if (inst && inst.setup) {
    inst.setup({ ctx, width, height, dpr: 1, aspectRatio: width / height }, defaultParams);
  }
  window.__art_instances['symphysodon-discus'] = inst;
}

const instance = window.__art_instances['symphysodon-discus'];
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
(xa)2+(yb)21,ystriation(x,w)=y0+Asin(kx+ωt+w)\left(\frac{x}{a}\right)^2 + \left(\frac{y}{b}\right)^2 \le 1, \quad y_{\text{striation}}(x, w) = y_0 + A \sin(k x + \omega t + w)
Click to expand
Compact Formula
disc_body = ellipse(r_x=0.52, r_y=0.54), heckel_bars = vertical(b)

Mathematical Tags

#discus #symphysodon #fish #cichlid #creatures #disciform #tropical #amazon
Author: Math Art Core Target: 60 FPS

Export & Embed: Royal Symphysodon Discus

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/symphysodon-discus" width="500" height="500" frameborder="0" loading="lazy"></iframe>
ESC
↑↓ Navigate Select
101 Mathematical Artworks

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! ✨