← 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>