← back to Model Arena

data/artifacts/02a362bd0b06/gpt.html

528 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Dragon Realm</title>
<style>
  html, body {
    margin: 0;
    padding: 0;
    overflow: hidden;
    background: #050816;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    color: #eee;
  }
  canvas {
    display: block;
  }

  /* Simple overlay UI */
  #ui {
    position: absolute;
    top: 12px;
    left: 50%;
    transform: translateX(-50%);
    text-align: center;
    color: #eee;
    text-shadow: 0 0 6px #000;
    pointer-events: none;
    font-size: 14px;
  }
  #ui h1 {
    margin: 0 0 4px;
    font-size: 18px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }
  #ui p {
    margin: 0;
  }
  #hint {
    margin-top: 6px;
    font-size: 13px;
    opacity: 0.9;
  }
  #hint span {
    padding: 3px 8px;
    border-radius: 999px;
    border: 1px solid rgba(255,255,255,0.3);
    background: radial-gradient(circle at 30% 20%, rgba(255,255,255,0.12), rgba(0,0,0,0.6));
    backdrop-filter: blur(2px);
    pointer-events: auto;
  }
</style>
</head>
<body>
<div id="ui">
  <h1>Dragon Realm</h1>
  <p>Winter skies over ancient mountains</p>
  <div id="hint">
    <span>Click anywhere to make the dragon breathe fire</span>
  </div>
</div>
<canvas id="c"></canvas>
<script>
(function () {
  const canvas = document.getElementById("c");
  const ctx = canvas.getContext("2d");
  let w, h, dpr;

  function resize() {
    dpr = window.devicePixelRatio || 1;
    w = window.innerWidth;
    h = window.innerHeight;
    canvas.width = w * dpr;
    canvas.height = h * dpr;
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  }
  window.addEventListener("resize", resize);
  resize();

  // Scene parameters
  let time = 0;
  let last = performance.now();

  // Snow particles
  const snowCount = 400;
  const snows = [];
  function initSnow() {
    snows.length = 0;
    for (let i = 0; i < snowCount; i++) {
      snows.push({
        x: Math.random() * w,
        y: Math.random() * h,
        r: Math.random() * 2 + 0.4,
        vy: 0.3 + Math.random() * 0.7,
        vx: -0.2 + Math.random() * 0.4,
        phase: Math.random() * Math.PI * 2
      });
    }
  }
  initSnow();

  // Mountain layers (procedural)
  const mountainLayers = [
    { color: "#050618", base: 0.68, amp: 80, detail: 0.0025, offset: 0 },
    { color: "#0a0d26", base: 0.70, amp: 100, detail: 0.003, offset: 500 },
    { color: "#111633", base: 0.77, amp: 130, detail: 0.004, offset: 1000 }
  ];

  function noise1D(x) {
    // Simple hash-based smooth noise
    const i = Math.floor(x);
    const f = x - i;
    const u = f * f * (3 - 2 * f);
    function hash(n) {
      const s = Math.sin(n * 127.1) * 43758.5453123;
      return s - Math.floor(s);
    }
    return (1 - u) * hash(i) + u * hash(i + 1);
  }

  // Dragon state
  const dragon = {
    baseY: 0.32,
    amp: 0.05,
    speed: 0.015,
    xNorm: 0.2,
    wobble: 0,
    fire: {
      active: false,
      t: 0
    }
  };

  let fireCooldown = 0;
  canvas.addEventListener("click", () => {
    if (!dragon.fire.active && fireCooldown <= 0) {
      dragon.fire.active = true;
      dragon.fire.t = 0;
      fireCooldown = 0.5;
    }
  });

  function update(dt) {
    time += dt;
    fireCooldown -= dt;
    if (dragon.fire.active) {
      dragon.fire.t += dt;
      if (dragon.fire.t > 0.75) {
        dragon.fire.active = false;
      }
    }

    // Snow update
    for (let p of snows) {
      p.phase += dt * 0.7;
      p.x += (p.vx + Math.sin(p.phase) * 0.15);
      p.y += p.vy * (0.8 + Math.sin(time * 0.5) * 0.2);
      if (p.y > h + 5) {
        p.y = -5;
        p.x = Math.random() * w;
      }
      if (p.x < -5) p.x = w + 5;
      if (p.x > w + 5) p.x = -5;
    }
  }

  function drawGradientSky() {
    const g = ctx.createLinearGradient(0, 0, 0, h);
    g.addColorStop(0, "#050516");
    g.addColorStop(0.35, "#080b24");
    g.addColorStop(0.7, "#141c3a");
    g.addColorStop(1, "#161828");
    ctx.fillStyle = g;
    ctx.fillRect(0, 0, w, h);
  }

  function drawMoonAndGlow() {
    const cx = w * 0.72;
    const cy = h * 0.22;
    const r = Math.min(w, h) * 0.09;

    // Glow
    let glowGrad = ctx.createRadialGradient(cx, cy, 0, cx, cy, r * 2.8);
    glowGrad.addColorStop(0, "rgba(230,240,255,0.6)");
    glowGrad.addColorStop(0.2, "rgba(210,230,255,0.4)");
    glowGrad.addColorStop(0.5, "rgba(120,160,255,0.08)");
    glowGrad.addColorStop(1, "rgba(0,0,0,0)");
    ctx.fillStyle = glowGrad;
    ctx.beginPath();
    ctx.arc(cx, cy, r * 2.8, 0, Math.PI * 2);
    ctx.fill();

    // Moon body
    let g = ctx.createRadialGradient(cx - r * 0.25, cy - r * 0.2, r * 0.1, cx, cy, r);
    g.addColorStop(0, "#ffffff");
    g.addColorStop(0.4, "#f5f7ff");
    g.addColorStop(1, "#cfd4ff");
    ctx.fillStyle = g;
    ctx.beginPath();
    ctx.arc(cx, cy, r, 0, Math.PI * 2);
    ctx.fill();

    // Subtle craters
    ctx.fillStyle = "rgba(180,190,225,0.6)";
    const craters = [
      { x: -0.25, y: -0.18, rr: 0.16 },
      { x: 0.2, y: 0.05, rr: 0.12 },
      { x: -0.05, y: 0.25, rr: 0.1 }
    ];
    for (let c of craters) {
      ctx.beginPath();
      ctx.arc(cx + c.x * r, cy + c.y * r, c.rr * r, 0, Math.PI * 2);
      ctx.fill();
    }
  }

  function drawStars() {
    const seed = 42;
    function rand(i) {
      const s = Math.sin(i * 12.9898 + seed * 78.233) * 43758.5453;
      return s - Math.floor(s);
    }
    const count = 220;
    ctx.save();
    for (let i = 0; i < count; i++) {
      const rx = rand(i * 1.2);
      const ry = rand(i * 2.3);
      const r = rand(i * 3.4) * 1.3 + 0.4;
      const tw = (Math.sin(time * 2 + i) + 1) * 0.5;
      const alpha = 0.25 + tw * 0.5;
      ctx.fillStyle = `rgba(230,240,255,${alpha.toFixed(3)})`;
      ctx.fillRect(rx * w, ry * h * 0.55, r, r);
    }
    ctx.restore();
  }

  function drawMountains() {
    for (let layer of mountainLayers) {
      ctx.fillStyle = layer.color;
      ctx.beginPath();
      ctx.moveTo(0, h);
      const baseY = h * layer.base;
      const freq = layer.detail;
      const amp = layer.amp;
      for (let x = 0; x <= w; x += 3) {
        const n = noise1D((x + layer.offset) * freq);
        const y = baseY - n * amp;
        ctx.lineTo(x, y);
      }
      ctx.lineTo(w, h);
      ctx.closePath();
      ctx.fill();

      // Subtle rim light from moon
      const moonX = w * 0.72;
      const moonY = h * 0.22;
      const grad = ctx.createLinearGradient(moonX, moonY, moonX, h);
      grad.addColorStop(0, "rgba(180,200,255,0.22)");
      grad.addColorStop(0.3, "rgba(120,150,220,0.16)");
      grad.addColorStop(0.8, "rgba(60,80,120,0.02)");
      grad.addColorStop(1, "rgba(0,0,0,0)");
      ctx.fillStyle = grad;
      ctx.globalCompositeOperation = "screen";
      ctx.fill();
      ctx.globalCompositeOperation = "source-over";
    }
  }

  function drawDragon() {
    const scale = Math.min(w, h) * 0.0012;
    const bob = Math.sin(time * 2) * dragon.amp * h;
    const flap = Math.sin(time * 8);
    const twist = Math.sin(time * 1.6) * 0.2;
    const cx = w * dragon.xNorm + Math.cos(time * dragon.speed * 60) * 8;
    const cy = h * dragon.baseY + bob;

    ctx.save();
    ctx.translate(cx, cy);
    ctx.rotate(-0.1 + twist * 0.1);

    // Body gradient
    const bodyGrad = ctx.createLinearGradient(-40 * scale, 0, 120 * scale, 0);
    bodyGrad.addColorStop(0, "#1b1d3c");
    bodyGrad.addColorStop(0.4, "#282c5a");
    bodyGrad.addColorStop(0.8, "#3e456d");
    bodyGrad.addColorStop(1, "#cbc9ff");

    // Body
    ctx.lineWidth = 10 * scale;
    ctx.lineCap = "round";
    ctx.strokeStyle = bodyGrad;
    ctx.beginPath();
    ctx.moveTo(-40 * scale, 10 * scale);
    ctx.quadraticCurveTo(10 * scale, -10 * scale, 90 * scale, 0);
    ctx.stroke();

    // Tail
    ctx.lineWidth = 7 * scale;
    ctx.strokeStyle = "#2b3055";
    ctx.beginPath();
    ctx.moveTo(70 * scale, 0);
    ctx.quadraticCurveTo(110 * scale, 15 * scale, 140 * scale, 0);
    ctx.stroke();
    ctx.fillStyle = "#cfd3ff";
    ctx.beginPath();
    ctx.moveTo(140 * scale, 0);
    ctx.lineTo(150 * scale, -10 * scale);
    ctx.lineTo(150 * scale, 10 * scale);
    ctx.closePath();
    ctx.fill();

    // Wings
    ctx.save();
    const wingFlap = flap * 0.6;
    const wingSpan = 120 * scale;
    const wingHeight = 55 * scale;

    function drawWing(side) {
      ctx.save();
      ctx.scale(side, 1);
      ctx.rotate(wingFlap * 0.5);
      const wingGrad = ctx.createLinearGradient(0, -wingHeight, 0, 20 * scale);
      wingGrad.addColorStop(0, "rgba(220,230,255,0.9)");
      wingGrad.addColorStop(0.4, "rgba(140,150,220,0.9)");
      wingGrad.addColorStop(1, "rgba(50,60,120,0.95)");

      ctx.fillStyle = wingGrad;
      ctx.strokeStyle = "rgba(240,250,255,0.8)";
      ctx.lineWidth = 2 * scale;

      ctx.beginPath();
      ctx.moveTo(-5 * scale, -5 * scale);
      ctx.quadraticCurveTo(wingSpan * 0.4, -wingHeight * 1.4, wingSpan, -5 * scale);
      ctx.quadraticCurveTo(wingSpan * 0.4, -wingHeight * 0.7, -5 * scale, -5 * scale);
      ctx.closePath();
      ctx.fill();
      ctx.stroke();

      // Wing veins
      ctx.lineWidth = 1.4 * scale;
      ctx.strokeStyle = "rgba(230,240,255,0.8)";
      ctx.beginPath();
      ctx.moveTo(-5 * scale, -5 * scale);
      ctx.lineTo(wingSpan * 0.55, -wingHeight * 1.0);
      ctx.stroke();

      ctx.beginPath();
      ctx.moveTo(wingSpan * 0.3, -wingHeight * 1.2);
      ctx.lineTo(wingSpan * 0.8, -wingHeight * 0.5);
      ctx.stroke();

      ctx.restore();
    }

    ctx.translate(-10 * scale, -5 * scale);
    ctx.globalAlpha = 0.9;
    drawWing(-1);
    drawWing(1);
    ctx.restore();

    // Head
    ctx.save();
    ctx.translate(-40 * scale, 10 * scale);
    ctx.rotate(-0.15);

    // Neck
    ctx.strokeStyle = bodyGrad;
    ctx.lineWidth = 7 * scale;
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.quadraticCurveTo(-25 * scale, -10 * scale, -45 * scale, -5 * scale);
    ctx.stroke();

    // Head shape
    const headGrad = ctx.createLinearGradient(-55 * scale, -20 * scale, 5 * scale, 10 * scale);
    headGrad.addColorStop(0, "#c9d0ff");
    headGrad.addColorStop(0.5, "#8f97e0");
    headGrad.addColorStop(1, "#3b3f73");
    ctx.fillStyle = headGrad;
    ctx.beginPath();
    ctx.moveTo(-55 * scale, -5 * scale);
    ctx.quadraticCurveTo(-30 * scale, -18 * scale, -5 * scale, -6 * scale);
    ctx.quadraticCurveTo(-10 * scale, 0, -5 * scale, 6 * scale);
    ctx.quadraticCurveTo(-30 * scale, 15 * scale, -55 * scale, 2 * scale);
    ctx.closePath();
    ctx.fill();

    // Jaw underside highlight
    ctx.strokeStyle = "rgba(245,250,255,0.85)";
    ctx.lineWidth = 2 * scale;
    ctx.beginPath();
    ctx.moveTo(-48 * scale, 2 * scale);
    ctx.quadraticCurveTo(-30 * scale, 10 * scale, -10 * scale, 5 * scale);
    ctx.stroke();

    // Horns
    ctx.strokeStyle = "#f5f5ff";
    ctx.lineWidth = 2 * scale;
    ctx.beginPath();
    ctx.moveTo(-40 * scale, -16 * scale);
    ctx.lineTo(-30 * scale, -30 * scale);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(-33 * scale, -15 * scale);
    ctx.lineTo(-20 * scale, -25 * scale);
    ctx.stroke();

    // Eye glow
    ctx.fillStyle = "#ffeb8a";
    ctx.beginPath();
    ctx.arc(-18 * scale, -4 * scale, 2.4 * scale, 0, Math.PI * 2);
    ctx.fill();
    ctx.fillStyle = "rgba(255,255,255,0.8)";
    ctx.beginPath();
    ctx.arc(-17.5 * scale, -4.5 * scale, 1 * scale, 0, Math.PI * 2);
    ctx.fill();

    // Nostrils
    ctx.fillStyle = "rgba(40,40,80,0.8)";
    ctx.beginPath();
    ctx.arc(-8 * scale, -1 * scale, 0.9 * scale, 0, Math.PI * 2);
    ctx.fill();

    // Fire breath
    if (dragon.fire.active) {
      const ft = dragon.fire.t;
      const intensity = Math.sin(Math.min(ft * Math.PI * 1.6, Math.PI));
      const len = 200 * scale * (0.3 + intensity * 1.6);
      const spread = 30 * scale * (0.4 + intensity * 0.8);
      const flicker = (Math.sin(time * 40) * 0.2 + Math.sin(time * 23.4) * 0.1) * spread;

      // Composite glow
      ctx.save();
      ctx.globalCompositeOperation = "lighter";

      // Outer glow
      let g = ctx.createLinearGradient(0, 0, -len, 0);
      g.addColorStop(0, "rgba(255,220,150,0.6)");
      g.addColorStop(0.3, "rgba(255,140,80,0.7)");
      g.addColorStop(0.7, "rgba(255,80,40,0.5)");
      g.addColorStop(1, "rgba(255,40,10,0.0)");
      ctx.fillStyle = g;

      ctx.beginPath();
      ctx.moveTo(-8 * scale, -1 * scale);
      ctx.quadraticCurveTo(-len * 0.4, -spread - flicker, -len, -4 * scale);
      ctx.quadraticCurveTo(-len * 0.5, spread + flicker, -8 * scale, 4 * scale);
      ctx.closePath();
      ctx.fill();

      // Inner hot core
      let g2 = ctx.createLinearGradient(0, 0, -len * 0.8, 0);
      g2.addColorStop(0, "rgba(255,255,255,0.98)");
      g2.addColorStop(0.35, "rgba(255,245,150,0.95)");
      g2.addColorStop(0.8, "rgba(255,120,40,0.0)");
      ctx.fillStyle = g2;

      ctx.beginPath();
      ctx.moveTo(-8 * scale, -1 * scale);
      ctx.quadraticCurveTo(-len * 0.35, -spread * 0.4 - flicker * 0.3, -len * 0.8, -1.5 * scale);
      ctx.quadraticCurveTo(-len * 0.45, spread * 0.3 + flicker * 0.2, -8 * scale, 2 * scale);
      ctx.closePath();
      ctx.fill();

      // Sparks
      ctx.fillStyle = "rgba(255,240,200,0.9)";
      for (let i = 0; i < 18; i++) {
        const t = Math.random();
        const x = -t * len * (0.5 + Math.random() * 0.5);
        const y = (Math.random() - 0.5) * (spread * 0.8 + 4 * scale);
        const r = (1 - t) * 2.2 * scale + 0.4 * scale;
        ctx.beginPath();
        ctx.arc(x, y, r, 0, Math.PI * 2);
        ctx.fill();
      }

      ctx.restore();
    }

    ctx.restore(); // head
    ctx.restore(); // dragon transform
  }

  function drawSnow() {
    ctx.save();
    ctx.globalCompositeOperation = "screen";
    for (let p of snows) {
      const twinkle = (Math.sin(time * 3 + p.phase * 3) + 1) * 0.5;
      const alpha = 0.25 + twinkle * 0.4;
      const size = p.r * (0.8 + twinkle * 0.6);
      ctx.fillStyle = `rgba(230,240,255,${alpha.toFixed(3)})`;
      ctx.beginPath();
      ctx.arc(p.x, p.y, size, 0, Math.PI * 2);
      ctx.fill();
    }
    ctx.restore();
  }

  function drawAtmosphericFog() {
    const g = ctx.createLinearGradient(0, h * 0.4, 0, h);
    g.addColorStop(0, "rgba(200,220,255,0.0)");
    g.addColorStop(0.5, "rgba(160,180,220,0.12)");
    g.addColorStop(1, "rgba(140,170,210,0.25)");
    ctx.fillStyle = g;
    ctx.fillRect(0, h * 0.4, w, h * 0.6);
  }

  function frame(t) {
    const dt = Math.min((t - last) / 1000, 0.033);
    last = t;
    update(dt);

    drawGradientSky();
    drawStars();
    drawMoonAndGlow();
    drawMountains();
    drawAtmosphericFog();
    drawDragon();
    drawSnow();

    requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
})();
</script>
</body>
</html>