97

Electric Radiant Lionfish

Marine reef simulation of Pterois radiata featuring thirteen elongated venomous dorsal spines with undulating luminous tips, fan-like zebra-banded pectoral ray wings, feathered supraocular horns, and slow predatory buoyancy.

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

Full Executable Algorithm Code

// 078 - Electric Radiant Lionfish (creatures)
// 1:1 Original algorithm engine source
function createElectricLionfish() {
  return {
    setup() {
    },
    render(context, timeState, params) {
      const { ctx, width, height } = context;
      const spineSpread = Number(params.spineLength ?? 1.1);
      const fanAgility = Number(params.pectoralWave ?? 0.85);
      const electricLuster = Number(params.bioluminescence ?? 1);
      const t = timeState.time * fanAgility;
      ctx.fillStyle = "#020509";
      ctx.fillRect(0, 0, width, height);
      const cx = width * 0.46;
      const cy = height * 0.5;
      const maxR = Math.min(width, height) * 0.44;
      const driftY = Math.sin(t * 1.8) * 6;
      const driftRot = Math.sin(t * 1.2) * 0.05;
      ctx.save();
      ctx.translate(cx, cy + driftY);
      ctx.rotate(driftRot);
      const amberHue = 24;
      const cyanGlowHue = 180;
      const spineCount = 13;
      for (let s = 0; s < spineCount; s++) {
        const sFrac = s / (spineCount - 1);
        const sAngle = -Math.PI * 0.65 + sFrac * Math.PI * 0.55;
        const sLen = maxR * (0.8 + 0.35 * Math.sin(sFrac * Math.PI)) * spineSpread;
        const sRootX = -maxR * 0.2 + sFrac * (maxR * 0.45);
        const sRootY = -maxR * 0.12;
        const sWave = Math.sin(t * 2.8 - s * 0.4) * (maxR * 0.08);
        const sTipX = sRootX + Math.cos(sAngle) * sLen + sWave;
        const sTipY = sRootY + Math.sin(sAngle) * sLen;
        ctx.beginPath();
        ctx.moveTo(sRootX, sRootY);
        ctx.quadraticCurveTo(sRootX + Math.cos(sAngle) * (sLen * 0.5), sRootY + Math.sin(sAngle) * (sLen * 0.5) - 10, sTipX, sTipY);
        ctx.strokeStyle = hsla(amberHue + s * 3, 90, 68, 0.95);
        ctx.lineWidth = 1.6;
        ctx.stroke();
        for (let b = 1; b <= 4; b++) {
          const bFrac = b / 5;
          const bx = sRootX + (sTipX - sRootX) * bFrac;
          const by = sRootY + (sTipY - sRootY) * bFrac;
          ctx.beginPath();
          ctx.arc(bx, by, 1.8, 0, Math.PI * 2);
          ctx.fillStyle = b % 2 === 0 ? "#ffffff" : "#080503";
          ctx.fill();
        }
        ctx.fillStyle = hsla(cyanGlowHue, 100, 85, 0.95 * electricLuster);
        ctx.beginPath();
        ctx.arc(sTipX, sTipY, 1.4, 0, Math.PI * 2);
        ctx.fill();
      }
      const tailLen = maxR * 0.42;
      const tailWave = Math.sin(t * 3.2) * 8;
      ctx.beginPath();
      ctx.moveTo(maxR * 0.35, 0);
      ctx.bezierCurveTo(maxR * 0.5, -maxR * 0.18, maxR * 0.7 + tailWave, -maxR * 0.18, maxR * 0.35 + tailLen + tailWave, 0);
      ctx.bezierCurveTo(maxR * 0.7 + tailWave, maxR * 0.18, maxR * 0.5, maxR * 0.18, maxR * 0.35, 0);
      ctx.fillStyle = "rgba(249, 115, 22, 0.2)";
      ctx.fill();
      ctx.strokeStyle = hsla(amberHue, 95, 70, 0.85);
      ctx.lineWidth = 1.2;
      ctx.stroke();
      const pectRays = 10;
      for (let pr = 0; pr < pectRays; pr++) {
        const prFrac = pr / (pectRays - 1);
        const pAng = Math.PI * 0.25 + prFrac * Math.PI * 0.65;
        const pWave = Math.sin(t * 3.5 - prFrac * 2.5) * 15;
        const pLen = maxR * (0.65 + 0.25 * Math.sin(prFrac * Math.PI)) * spineSpread;
        const rootX = -maxR * 0.05;
        const rootY = maxR * 0.05;
        const tipX = rootX + Math.cos(pAng) * pLen + pWave;
        const tipY = rootY + Math.sin(pAng) * pLen + pWave * 0.5;
        ctx.beginPath();
        ctx.moveTo(rootX, rootY);
        ctx.quadraticCurveTo(rootX + Math.cos(pAng) * (pLen * 0.5), rootY + Math.sin(pAng) * (pLen * 0.5), tipX, tipY);
        ctx.strokeStyle = hsla(amberHue, 95, 62, 0.9);
        ctx.lineWidth = 1.5;
        ctx.stroke();
        ctx.fillStyle = hsla(amberHue + pr * 5, 90, 50, 0.08 * electricLuster);
        ctx.fill();
        ctx.fillStyle = hsla(cyanGlowHue, 100, 85, 0.85 * electricLuster);
        ctx.beginPath();
        ctx.arc(tipX, tipY, 1.3, 0, Math.PI * 2);
        ctx.fill();
      }
      ctx.beginPath();
      ctx.moveTo(-maxR * 0.35, -maxR * 0.02);
      ctx.bezierCurveTo(-maxR * 0.2, -maxR * 0.22, maxR * 0.15, -maxR * 0.18, maxR * 0.35, 0);
      ctx.bezierCurveTo(maxR * 0.15, maxR * 0.18, -maxR * 0.2, maxR * 0.22, -maxR * 0.35, -maxR * 0.02);
      ctx.closePath();
      const bodyGrad = ctx.createLinearGradient(-maxR * 0.35, 0, maxR * 0.35, 0);
      bodyGrad.addColorStop(0, "#1c0803");
      bodyGrad.addColorStop(0.4, "#c2410c");
      bodyGrad.addColorStop(0.8, "#7c2d12");
      bodyGrad.addColorStop(1, "#1c0803");
      ctx.fillStyle = bodyGrad;
      ctx.fill();
      ctx.strokeStyle = "#fb923c";
      ctx.lineWidth = 1.6;
      ctx.stroke();
      for (let s = 1; s <= 7; s++) {
        const sx = -maxR * 0.28 + s * (maxR * 0.08);
        ctx.beginPath();
        ctx.moveTo(sx, -maxR * 0.12);
        ctx.quadraticCurveTo(sx + 6, 0, sx, maxR * 0.12);
        ctx.strokeStyle = "#fff7ed";
        ctx.lineWidth = 2;
        ctx.stroke();
      }
      for (const hSide of [-1, 1]) {
        const hSway = Math.sin(t * 3.5 + hSide) * 3;
        ctx.beginPath();
        ctx.moveTo(-maxR * 0.28, -maxR * 0.06);
        ctx.quadraticCurveTo(-maxR * 0.34 + hSway, -maxR * 0.22, -maxR * 0.32 + hSway, -maxR * 0.28);
        ctx.strokeStyle = "#fdba74";
        ctx.lineWidth = 1.4;
        ctx.stroke();
      }
      const eyeX = -maxR * 0.26;
      const eyeY = -maxR * 0.03;
      ctx.beginPath();
      ctx.arc(eyeX, eyeY, 4.5, 0, Math.PI * 2);
      ctx.fillStyle = "#451a03";
      ctx.fill();
      ctx.strokeStyle = "#f97316";
      ctx.lineWidth = 1.2;
      ctx.stroke();
      ctx.beginPath();
      ctx.arc(eyeX, eyeY, 2, 0, Math.PI * 2);
      ctx.fillStyle = "#000000";
      ctx.fill();
      ctx.fillStyle = "#ffffff";
      ctx.beginPath();
      ctx.arc(eyeX - 0.9, eyeY - 0.9, 0.8, 0, Math.PI * 2);
      ctx.fill();
      ctx.restore();
    }
  };
}

// Default parameters from content metadata
const defaultParams = [
  {
    "key": "spineLength",
    "label": "Dorsal Spines Spread",
    "type": "range",
    "min": 0.6,
    "max": 1.5,
    "step": 0.1,
    "defaultValue": 1.1,
    "description": "Elongated venomous spine length and arc"
  },
  {
    "key": "pectoralWave",
    "label": "Pectoral Fan Motion",
    "type": "range",
    "min": 0.4,
    "max": 1.8,
    "step": 0.05,
    "defaultValue": 0.85,
    "description": "Pectoral fin ray oscillation cadence"
  },
  {
    "key": "bioluminescence",
    "label": "Needle Tip Glow",
    "type": "range",
    "min": 0.3,
    "max": 1.6,
    "step": 0.1,
    "defaultValue": 1,
    "description": "Cyan glow luminescence at spine tips"
  }
];

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

const instance = window.__art_instances['electric-lionfish'];
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
pspine(s)=pdorsal+Ls[cosθssinθs]+wdrift(t),pwing(r)=fanRay(r,t)\mathbf{p}_{\text{spine}}(s) = \mathbf{p}_{\text{dorsal}} + L_s \begin{bmatrix} \cos\theta_s \\ \sin\theta_s \end{bmatrix} + \mathbf{w}_{\text{drift}}(t), \quad \mathbf{p}_{\text{wing}}(r) = \text{fanRay}(r, t)
Click to expand
Compact Formula
spine = line(root, angle, len) + wave(t), pectoral_fan = 10_rays(angle, t)

Mathematical Tags

#lionfish #fish #pterois #creatures #spines #reef #venomous #marine
Author: Math Art Core Target: 60 FPS

Export & Embed: Electric Radiant Lionfish

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/electric-lionfish" 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! ✨