← back to Model Arena
auto-save: 2026-07-23T10:50:25 (14 files) — data/challenges.json data/costlog.jsonl data/votes.jsonl data/artifacts/48026228790d/hf-qwen-coder-32b.html data/artifacts/48026228790d/hf-qwen-coder-32b.png
5b59fd46f9845d0c604d3b56ea9f676d1c3f3c32 · 2026-07-23 10:50:34 -0700 · Steve Abrams
Files touched
A data/artifacts/48026228790d/hf-qwen-coder-32b.htmlA data/artifacts/48026228790d/hf-qwen-coder-32b.pngA data/artifacts/48026228790d/kimi.htmlA data/artifacts/48026228790d/kimi.pngA data/artifacts/52fc117697fd/gemma3-12b.htmlA data/artifacts/52fc117697fd/gemma3-12b.pngA data/artifacts/52fc117697fd/gpt.htmlA data/artifacts/52fc117697fd/gpt.pngA data/artifacts/52fc117697fd/grok.htmlA data/artifacts/52fc117697fd/grok.pngA data/artifacts/52fc117697fd/hermes3-8b.htmlA data/artifacts/52fc117697fd/hermes3-8b.pngA data/artifacts/52fc117697fd/hf-qwen-coder-32b.htmlA data/artifacts/52fc117697fd/hf-qwen-coder-32b.pngA data/artifacts/52fc117697fd/kimi.htmlA data/artifacts/52fc117697fd/kimi.pngA data/artifacts/52fc117697fd/qwen25-7b.htmlA data/artifacts/52fc117697fd/qwen25-7b.pngA data/artifacts/52fc117697fd/qwen3-14b.htmlA data/artifacts/52fc117697fd/qwen3-14b.pngA data/artifacts/673cf6b31c78/claude-code.htmlA data/artifacts/673cf6b31c78/claude-code.pngA data/artifacts/673cf6b31c78/gemma3-12b.htmlA data/artifacts/673cf6b31c78/gemma3-12b.pngA data/artifacts/673cf6b31c78/gpt.htmlA data/artifacts/673cf6b31c78/gpt.pngA data/artifacts/673cf6b31c78/grok.htmlA data/artifacts/673cf6b31c78/grok.pngA data/artifacts/673cf6b31c78/hermes3-8b.htmlA data/artifacts/673cf6b31c78/hermes3-8b.pngA data/artifacts/673cf6b31c78/hf-qwen-coder-32b.htmlA data/artifacts/673cf6b31c78/hf-qwen-coder-32b.pngA data/artifacts/673cf6b31c78/kimi.htmlA data/artifacts/673cf6b31c78/kimi.pngA data/artifacts/673cf6b31c78/qwen25-7b.htmlA data/artifacts/673cf6b31c78/qwen25-7b.pngA data/artifacts/673cf6b31c78/qwen3-14b.htmlA data/artifacts/673cf6b31c78/qwen3-14b.pngA data/artifacts/9ba7a5497a50/claude-code.htmlA data/artifacts/9ba7a5497a50/claude-code.pngA data/artifacts/9ba7a5497a50/gemma3-12b.htmlA data/artifacts/9ba7a5497a50/gemma3-12b.pngA data/artifacts/9ba7a5497a50/gpt.htmlA data/artifacts/9ba7a5497a50/gpt.pngA data/artifacts/9ba7a5497a50/grok.htmlA data/artifacts/9ba7a5497a50/grok.pngA data/artifacts/9ba7a5497a50/hermes3-8b.htmlA data/artifacts/9ba7a5497a50/hermes3-8b.pngA data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.htmlA data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.pngA data/artifacts/9ba7a5497a50/kimi.htmlA data/artifacts/9ba7a5497a50/kimi.pngA data/artifacts/9ba7a5497a50/qwen25-7b.htmlA data/artifacts/9ba7a5497a50/qwen25-7b.pngA data/artifacts/9ba7a5497a50/qwen3-14b.htmlA data/artifacts/9ba7a5497a50/qwen3-14b.pngA data/artifacts/a3958f97e739/claude-code.htmlA data/artifacts/a3958f97e739/claude-code.pngA data/artifacts/a3958f97e739/gemma3-12b.htmlA data/artifacts/a3958f97e739/gemma3-12b.pngA data/artifacts/a3958f97e739/gpt.htmlA data/artifacts/a3958f97e739/gpt.pngA data/artifacts/a3958f97e739/grok.htmlA data/artifacts/a3958f97e739/grok.pngA data/artifacts/a3958f97e739/hermes3-8b.htmlA data/artifacts/a3958f97e739/hermes3-8b.pngA data/artifacts/a3958f97e739/hf-qwen-coder-32b.htmlA data/artifacts/a3958f97e739/hf-qwen-coder-32b.pngA data/artifacts/a3958f97e739/kimi.htmlA data/artifacts/a3958f97e739/kimi.pngA data/artifacts/a3958f97e739/qwen25-7b.htmlA data/artifacts/a3958f97e739/qwen25-7b.pngA data/artifacts/a3958f97e739/qwen3-14b.htmlA data/artifacts/a3958f97e739/qwen3-14b.pngA data/artifacts/b14c4e0ccc4e/claude-code.htmlA data/artifacts/b14c4e0ccc4e/claude-code.pngA data/artifacts/b14c4e0ccc4e/gemma3-12b.htmlA data/artifacts/b14c4e0ccc4e/gemma3-12b.pngA data/artifacts/b14c4e0ccc4e/gpt.htmlA data/artifacts/b14c4e0ccc4e/gpt.pngA data/artifacts/b14c4e0ccc4e/grok.htmlA data/artifacts/b14c4e0ccc4e/grok.pngA data/artifacts/b14c4e0ccc4e/hermes3-8b.htmlA data/artifacts/b14c4e0ccc4e/hermes3-8b.pngA data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.htmlA data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.pngA data/artifacts/b14c4e0ccc4e/kimi.htmlA data/artifacts/b14c4e0ccc4e/kimi.pngA data/artifacts/b14c4e0ccc4e/qwen25-7b.htmlA data/artifacts/b14c4e0ccc4e/qwen25-7b.pngA data/artifacts/b14c4e0ccc4e/qwen3-14b.htmlA data/artifacts/b14c4e0ccc4e/qwen3-14b.pngA data/artifacts/e85044630905/claude-code.htmlA data/artifacts/e85044630905/claude-code.pngA data/artifacts/e85044630905/gemma3-12b.htmlA data/artifacts/e85044630905/gemma3-12b.pngA data/artifacts/e85044630905/gpt.htmlA data/artifacts/e85044630905/gpt.pngA data/artifacts/e85044630905/grok.htmlA data/artifacts/e85044630905/grok.pngA data/artifacts/e85044630905/hermes3-8b.htmlA data/artifacts/e85044630905/hermes3-8b.pngA data/artifacts/e85044630905/hf-qwen-coder-32b.htmlA data/artifacts/e85044630905/hf-qwen-coder-32b.pngA data/artifacts/e85044630905/kimi.htmlA data/artifacts/e85044630905/kimi.pngA data/artifacts/e85044630905/qwen25-7b.htmlA data/artifacts/e85044630905/qwen25-7b.pngA data/artifacts/e85044630905/qwen3-14b.htmlA data/artifacts/e85044630905/qwen3-14b.pngA data/artifacts/f83743a0fa7e/gemma3-12b.htmlA data/artifacts/f83743a0fa7e/gemma3-12b.pngA data/artifacts/f83743a0fa7e/grok.htmlA data/artifacts/f83743a0fa7e/grok.pngA data/artifacts/f83743a0fa7e/qwen25-7b.htmlA data/artifacts/f83743a0fa7e/qwen25-7b.pngA data/artifacts/f83743a0fa7e/qwen3-14b.htmlA data/artifacts/f83743a0fa7e/qwen3-14b.pngM data/challenges.jsonM data/costlog.jsonlM data/votes.jsonl
Diff
commit 5b59fd46f9845d0c604d3b56ea9f676d1c3f3c32
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 23 10:50:34 2026 -0700
auto-save: 2026-07-23T10:50:25 (14 files) — data/challenges.json data/costlog.jsonl data/votes.jsonl data/artifacts/48026228790d/hf-qwen-coder-32b.html data/artifacts/48026228790d/hf-qwen-coder-32b.png
---
data/artifacts/48026228790d/hf-qwen-coder-32b.html | 180 +++
data/artifacts/48026228790d/hf-qwen-coder-32b.png | Bin 0 -> 5084 bytes
data/artifacts/48026228790d/kimi.html | 313 +++++
data/artifacts/48026228790d/kimi.png | Bin 0 -> 205211 bytes
data/artifacts/52fc117697fd/gemma3-12b.html | 138 +++
data/artifacts/52fc117697fd/gemma3-12b.png | Bin 0 -> 5977 bytes
data/artifacts/52fc117697fd/gpt.html | 321 +++++
data/artifacts/52fc117697fd/gpt.png | Bin 0 -> 68919 bytes
data/artifacts/52fc117697fd/grok.html | 250 ++++
data/artifacts/52fc117697fd/grok.png | Bin 0 -> 25018 bytes
data/artifacts/52fc117697fd/hermes3-8b.html | 114 ++
data/artifacts/52fc117697fd/hermes3-8b.png | Bin 0 -> 2803 bytes
data/artifacts/52fc117697fd/hf-qwen-coder-32b.html | 95 ++
data/artifacts/52fc117697fd/hf-qwen-coder-32b.png | Bin 0 -> 4896 bytes
data/artifacts/52fc117697fd/kimi.html | 241 ++++
data/artifacts/52fc117697fd/kimi.png | Bin 0 -> 7145 bytes
data/artifacts/52fc117697fd/qwen25-7b.html | 77 ++
data/artifacts/52fc117697fd/qwen25-7b.png | Bin 0 -> 2728 bytes
data/artifacts/52fc117697fd/qwen3-14b.html | 172 +++
data/artifacts/52fc117697fd/qwen3-14b.png | Bin 0 -> 6101 bytes
data/artifacts/673cf6b31c78/claude-code.html | 443 +++++++
data/artifacts/673cf6b31c78/claude-code.png | Bin 0 -> 165874 bytes
data/artifacts/673cf6b31c78/gemma3-12b.html | 188 +++
data/artifacts/673cf6b31c78/gemma3-12b.png | Bin 0 -> 11281 bytes
data/artifacts/673cf6b31c78/gpt.html | 1247 ++++++++++++++++++++
data/artifacts/673cf6b31c78/gpt.png | Bin 0 -> 285007 bytes
data/artifacts/673cf6b31c78/grok.html | 665 +++++++++++
data/artifacts/673cf6b31c78/grok.png | Bin 0 -> 164858 bytes
data/artifacts/673cf6b31c78/hermes3-8b.html | 194 +++
data/artifacts/673cf6b31c78/hermes3-8b.png | Bin 0 -> 8918 bytes
data/artifacts/673cf6b31c78/hf-qwen-coder-32b.html | 104 ++
data/artifacts/673cf6b31c78/hf-qwen-coder-32b.png | Bin 0 -> 8578 bytes
data/artifacts/673cf6b31c78/kimi.html | 434 +++++++
data/artifacts/673cf6b31c78/kimi.png | Bin 0 -> 13733 bytes
data/artifacts/673cf6b31c78/qwen25-7b.html | 114 ++
data/artifacts/673cf6b31c78/qwen25-7b.png | Bin 0 -> 2820 bytes
data/artifacts/673cf6b31c78/qwen3-14b.html | 77 ++
data/artifacts/673cf6b31c78/qwen3-14b.png | Bin 0 -> 10765 bytes
data/artifacts/9ba7a5497a50/claude-code.html | 359 ++++++
data/artifacts/9ba7a5497a50/claude-code.png | Bin 0 -> 71056 bytes
data/artifacts/9ba7a5497a50/gemma3-12b.html | 182 +++
data/artifacts/9ba7a5497a50/gemma3-12b.png | Bin 0 -> 10565 bytes
data/artifacts/9ba7a5497a50/gpt.html | 826 +++++++++++++
data/artifacts/9ba7a5497a50/gpt.png | Bin 0 -> 407886 bytes
data/artifacts/9ba7a5497a50/grok.html | 665 +++++++++++
data/artifacts/9ba7a5497a50/grok.png | Bin 0 -> 44949 bytes
data/artifacts/9ba7a5497a50/hermes3-8b.html | 185 +++
data/artifacts/9ba7a5497a50/hermes3-8b.png | Bin 0 -> 15198 bytes
data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.html | 147 +++
data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.png | Bin 0 -> 18871 bytes
data/artifacts/9ba7a5497a50/kimi.html | 493 ++++++++
data/artifacts/9ba7a5497a50/kimi.png | Bin 0 -> 58360 bytes
data/artifacts/9ba7a5497a50/qwen25-7b.html | 133 +++
data/artifacts/9ba7a5497a50/qwen25-7b.png | Bin 0 -> 21681 bytes
data/artifacts/9ba7a5497a50/qwen3-14b.html | 251 ++++
data/artifacts/9ba7a5497a50/qwen3-14b.png | Bin 0 -> 20567 bytes
data/artifacts/a3958f97e739/claude-code.html | 228 ++++
data/artifacts/a3958f97e739/claude-code.png | Bin 0 -> 96046 bytes
data/artifacts/a3958f97e739/gemma3-12b.html | 160 +++
data/artifacts/a3958f97e739/gemma3-12b.png | Bin 0 -> 6172 bytes
data/artifacts/a3958f97e739/gpt.html | 544 +++++++++
data/artifacts/a3958f97e739/gpt.png | Bin 0 -> 32462 bytes
data/artifacts/a3958f97e739/grok.html | 393 ++++++
data/artifacts/a3958f97e739/grok.png | Bin 0 -> 176821 bytes
data/artifacts/a3958f97e739/hermes3-8b.html | 143 +++
data/artifacts/a3958f97e739/hermes3-8b.png | Bin 0 -> 13095 bytes
data/artifacts/a3958f97e739/hf-qwen-coder-32b.html | 96 ++
data/artifacts/a3958f97e739/hf-qwen-coder-32b.png | Bin 0 -> 5693 bytes
data/artifacts/a3958f97e739/kimi.html | 326 +++++
data/artifacts/a3958f97e739/kimi.png | Bin 0 -> 24343 bytes
data/artifacts/a3958f97e739/qwen25-7b.html | 81 ++
data/artifacts/a3958f97e739/qwen25-7b.png | Bin 0 -> 2838 bytes
data/artifacts/a3958f97e739/qwen3-14b.html | 213 ++++
data/artifacts/a3958f97e739/qwen3-14b.png | Bin 0 -> 8214 bytes
data/artifacts/b14c4e0ccc4e/claude-code.html | 474 ++++++++
data/artifacts/b14c4e0ccc4e/claude-code.png | Bin 0 -> 234674 bytes
data/artifacts/b14c4e0ccc4e/gemma3-12b.html | 134 +++
data/artifacts/b14c4e0ccc4e/gemma3-12b.png | Bin 0 -> 13031 bytes
data/artifacts/b14c4e0ccc4e/gpt.html | 953 +++++++++++++++
data/artifacts/b14c4e0ccc4e/gpt.png | Bin 0 -> 334633 bytes
data/artifacts/b14c4e0ccc4e/grok.html | 492 ++++++++
data/artifacts/b14c4e0ccc4e/grok.png | Bin 0 -> 310765 bytes
data/artifacts/b14c4e0ccc4e/hermes3-8b.html | 127 ++
data/artifacts/b14c4e0ccc4e/hermes3-8b.png | Bin 0 -> 20080 bytes
data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.html | 176 +++
data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.png | Bin 0 -> 22228 bytes
data/artifacts/b14c4e0ccc4e/kimi.html | 397 +++++++
data/artifacts/b14c4e0ccc4e/kimi.png | Bin 0 -> 213565 bytes
data/artifacts/b14c4e0ccc4e/qwen25-7b.html | 169 +++
data/artifacts/b14c4e0ccc4e/qwen25-7b.png | Bin 0 -> 19757 bytes
data/artifacts/b14c4e0ccc4e/qwen3-14b.html | 155 +++
data/artifacts/b14c4e0ccc4e/qwen3-14b.png | Bin 0 -> 139656 bytes
data/artifacts/e85044630905/claude-code.html | 244 ++++
data/artifacts/e85044630905/claude-code.png | Bin 0 -> 237404 bytes
data/artifacts/e85044630905/gemma3-12b.html | 216 ++++
data/artifacts/e85044630905/gemma3-12b.png | Bin 0 -> 8130 bytes
data/artifacts/e85044630905/gpt.html | 377 ++++++
data/artifacts/e85044630905/gpt.png | Bin 0 -> 29808 bytes
data/artifacts/e85044630905/grok.html | 410 +++++++
data/artifacts/e85044630905/grok.png | Bin 0 -> 224231 bytes
data/artifacts/e85044630905/hermes3-8b.html | 180 +++
data/artifacts/e85044630905/hermes3-8b.png | Bin 0 -> 5565 bytes
data/artifacts/e85044630905/hf-qwen-coder-32b.html | 104 ++
data/artifacts/e85044630905/hf-qwen-coder-32b.png | Bin 0 -> 14008 bytes
data/artifacts/e85044630905/kimi.html | 367 ++++++
data/artifacts/e85044630905/kimi.png | Bin 0 -> 34049 bytes
data/artifacts/e85044630905/qwen25-7b.html | 113 ++
data/artifacts/e85044630905/qwen25-7b.png | Bin 0 -> 4441 bytes
data/artifacts/e85044630905/qwen3-14b.html | 374 ++++++
data/artifacts/e85044630905/qwen3-14b.png | Bin 0 -> 10411 bytes
data/artifacts/f83743a0fa7e/gemma3-12b.html | 58 +
data/artifacts/f83743a0fa7e/gemma3-12b.png | Bin 0 -> 62656 bytes
data/artifacts/f83743a0fa7e/grok.html | 732 ++++++++++++
data/artifacts/f83743a0fa7e/grok.png | Bin 0 -> 118591 bytes
data/artifacts/f83743a0fa7e/qwen25-7b.html | 34 +
data/artifacts/f83743a0fa7e/qwen25-7b.png | Bin 0 -> 41135 bytes
data/artifacts/f83743a0fa7e/qwen3-14b.html | 93 ++
data/artifacts/f83743a0fa7e/qwen3-14b.png | Bin 0 -> 81022 bytes
data/challenges.json | 1247 +++++++++++++++++++-
data/costlog.jsonl | 20 +
data/votes.jsonl | 6 +
121 files changed, 18428 insertions(+), 16 deletions(-)
diff --git a/data/artifacts/48026228790d/hf-qwen-coder-32b.html b/data/artifacts/48026228790d/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..65a75ae
--- /dev/null
+++ b/data/artifacts/48026228790d/hf-qwen-coder-32b.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Voronoi Shatter Effect</title>
+<style>
+ body { margin: 0; overflow: hidden; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f0f0; }
+ canvas { cursor: crosshair; }
+ button { position: absolute; top: 10px; left: 10px; z-index: 10; }
+</style>
+</head>
+<body>
+<canvas id="voronoiCanvas"></canvas>
+<button onclick="togglePoints()">Show/Hide Points</button>
+<script>
+const canvas = document.getElementById('voronoiCanvas');
+const ctx = canvas.getContext('2d');
+let points = [];
+let showPoints = true;
+
+function resizeCanvas() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+}
+
+window.addEventListener('resize', resizeCanvas);
+resizeCanvas();
+
+function generateGradient(cell, color1, color2) {
+ const gradient = ctx.createLinearGradient(cell[0], cell[1], cell[2], cell[3]);
+ gradient.addColorStop(0, color1);
+ gradient.addColorStop(1, color2);
+ return gradient;
+}
+
+function drawCell(cell, points) {
+ if (cell.length < 3) return;
+ ctx.beginPath();
+ ctx.moveTo(points[cell[0]][0], points[cell[0]][1]);
+ for (let i = 1; i < cell.length; i++) {
+ ctx.lineTo(points[cell[i]][0], points[cell[i]][1]);
+ }
+ ctx.closePath();
+ const gradient = generateGradient(cell, `hsl(${Math.random() * 360}, 80%, 50%)`, `hsl(${Math.random() * 360}, 80%, 50%)`);
+ ctx.fillStyle = gradient;
+ ctx.fill();
+}
+
+function drawPoints(points) {
+ points.forEach(point => {
+ ctx.beginPath();
+ ctx.arc(point[0], point[1], 2, 0, Math.PI * 2);
+ ctx.fillStyle = 'black';
+ ctx.fill();
+ });
+}
+
+function computeVoronoi() {
+ const sites = points.map(p => ({ x: p[0], y: p[1] }));
+ const bbox = { xl: 0, xr: canvas.width, yt: 0, yb: canvas.height };
+ const voronoi = new Voronoi();
+ const diagram = voronoi.compute(sites, bbox);
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ if (diagram.cells) {
+ for (const cell of Object.values(diagram.cells)) {
+ drawCell(cell.halfedges.map(edge => edge.getStartpoint()), points);
+ }
+ }
+ if (showPoints) {
+ drawPoints(points);
+ }
+}
+
+function addPoint(event) {
+ const rect = canvas.getBoundingClientRect();
+ const x = event.clientX - rect.left;
+ const y = event.clientY - rect.top;
+ points.push([x, y]);
+ computeVoronoi();
+}
+
+canvas.addEventListener('click', addPoint);
+
+function togglePoints() {
+ showPoints = !showPoints;
+ computeVoronoi();
+}
+
+class Voronoi {
+ constructor() {}
+
+ compute(sites, bbox) {
+ const cells = {};
+ const edges = [];
+ sites.forEach(site => { cells[site.x + ',' + site.y] = { site, halfedges: [] }; });
+
+ for (let i = 0; i < sites.length - 1; i++) {
+ for (let j = i + 1; j < sites.length; j++) {
+ const edge = this.bisect(sites[i], sites[j]);
+ if (!edge) continue;
+ edges.push(edge);
+ cells[sites[i].x + ',' + sites[i].y].halfedges.push({ edge, site: sites[i] });
+ cells[sites[j].x + ',' + sites[j].y].halfedges.push({ edge, site: sites[j] });
+ }
+ }
+
+ for (const edge of edges) {
+ if (!edge.clippedVertices[0]) edge.clippedVertices[0] = this.clipEdge(edge, bbox);
+ if (!edge.clippedVertices[1]) edge.clippedVertices[1] = this.clipEdge(edge, bbox);
+ if (!edge.clippedVertices[0] || !edge.clippedVertices[1]) continue;
+ const va = edge.clippedVertices[0], vb = edge.clippedVertices[1];
+ edge.getStartpoint = () => va;
+ }
+
+ return { cells };
+ }
+
+ bisect(p, q) {
+ const dx = q.x - p.x, dy = q.y - p.y, dx2 = dx * .5, dy2 = dy * .5;
+ const cx = p.x + dx2, cy = p.y + dy2, m = (p.x !== q.x) ? -(dy / dx) : undefined;
+ const b = cy - (m * cx);
+ let leftRegion, rightRegion;
+
+ if (m === undefined) {
+ leftRegion = { x: cx };
+ rightRegion = { x: cx };
+ } else if (m === 0) {
+ leftRegion = { y: cy };
+ rightRegion = { y: cy };
+ } else {
+ const pxmy = p.x - m * p.y, pyxm = p.y + m * p.x;
+ leftRegion = { x: pxmy, y: pyxm };
+ rightRegion = { x: pxmy, y: pyxm };
+ }
+
+ return {
+ lSite: p,
+ rSite: q,
+ edge: [leftRegion, rightRegion],
+ clippedVertices: []
+ };
+ }
+
+ clipEdge(edge, bbox) {
+ const ax = edge.edge[0].x, ay = edge.edge[0].y;
+ const bx = edge.edge[1].x, by = edge.edge[1].y;
+ let a = false, b = false;
+ const p = [ax, ay], q = [bx, by];
+
+ if (this.clipLine(p, q, bbox.xl, ax < bx ? 1 : -1, 'x')) { a = true; }
+ if (this.clipLine(q, p, bbox.xr, ax < bx ? 1 : -1, 'x')) { b = true; }
+
+ if (a && b) return null;
+
+ if (this.clipLine(p, q, bbox.yt, ay < by ? 1 : -1, 'y')) { a = true; }
+ if (this.clipLine(q, p, bbox.yb, ay < by ? 1 : -1, 'y')) { b = true; }
+
+ if (a && b) return null;
+
+ return [p[0], p[1]];
+ }
+
+ clipLine(p, q, bound, direction, axis) {
+ const c0 = p[axis] - bound;
+ const c1 = q[axis] - bound;
+ if (c0 * c1 >= 0) return true; // both points on same side of the line
+ if (c0 === 0 && c1 === 0) return false; // both points are on the boundary
+
+ const u = c0 / (c0 - c1);
+ p[0] += direction * u * (q[0] - p[0]);
+ p[1] += direction * u * (q[1] - p[1]);
+ return false;
+ }
+}
+
+computeVoronoi();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/48026228790d/hf-qwen-coder-32b.png b/data/artifacts/48026228790d/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..8e6df67
Binary files /dev/null and b/data/artifacts/48026228790d/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/48026228790d/kimi.html b/data/artifacts/48026228790d/kimi.html
new file mode 100644
index 0000000..d349469
--- /dev/null
+++ b/data/artifacts/48026228790d/kimi.html
@@ -0,0 +1,313 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Interactive Voronoi Shatter</title>
+<style>
+body {
+ margin: 0;
+ overflow: hidden;
+ background: #050505;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+}
+canvas {
+ display: block;
+ width: 100vw;
+ height: 100vh;
+ cursor: crosshair;
+}
+.controls {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ background: rgba(20, 20, 20, 0.9);
+ backdrop-filter: blur(10px);
+ padding: 20px;
+ border-radius: 12px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: #fff;
+ box-shadow: 0 4px 20px rgba(0,0,0,0.5);
+ user-select: none;
+}
+button {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border: none;
+ color: white;
+ padding: 10px 18px;
+ margin: 5px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 13px;
+ font-weight: 600;
+ transition: all 0.2s;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+button:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
+}
+button:active {
+ transform: translateY(0);
+}
+.info {
+ margin-top: 12px;
+ font-size: 11px;
+ opacity: 0.6;
+ line-height: 1.4;
+}
+</style>
+</head>
+<body>
+<canvas id="canvas"></canvas>
+<div class="controls">
+ <button id="togglePoints">Hide Points</button>
+ <button id="reset">Reset</button>
+ <div class="info">Click anywhere to shatter<br>Cells: <span id="count">0</span></div>
+</div>
+<script>
+const canvas = document.getElementById('canvas');
+const ctx = canvas.getContext('2d');
+let width, height;
+let points = [];
+let showPoints = true;
+let animating = false;
+let animationFrame = 0;
+const ANIMATION_DURATION = 40;
+
+// Initialize with random points
+function init() {
+ resize();
+ // Create initial scattered points
+ for (let i = 0; i < 8; i++) {
+ points.push({
+ x: Math.random() * width * 0.8 + width * 0.1,
+ y: Math.random() * height * 0.8 + height * 0.1,
+ hue: Math.random() * 360,
+ born: -100 // negative means fully grown
+ });
+ }
+ render();
+}
+
+function resize() {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ if (!animating) render();
+}
+
+window.addEventListener('resize', resize);
+
+// Clip polygon to half-plane (closer to p1 than p2)
+function clipPolygon(poly, p1, p2) {
+ if (poly.length === 0) return [];
+ const newPoly = [];
+ const mx = (p1.x + p2.x) / 2;
+ const my = (p1.y + p2.y) / 2;
+ const dx = p2.x - p1.x;
+ const dy = p2.y - p1.y;
+
+ for (let i = 0; i < poly.length; i++) {
+ const curr = poly[i];
+ const next = poly[(i + 1) % poly.length];
+ const currVal = (curr[0] - mx) * dx + (curr[1] - my) * dy;
+ const nextVal = (next[0] - mx) * dx + (next[1] - my) * dy;
+ const currIn = currVal <= 0;
+ const nextIn = nextVal <= 0;
+
+ if (currIn && nextIn) {
+ newPoly.push(next);
+ } else if (currIn && !nextIn) {
+ // Leaving half-plane: add intersection
+ const t = currVal / (currVal - nextVal);
+ if (t >= 0 && t <= 1) {
+ newPoly.push([
+ curr[0] + (next[0] - curr[0]) * t,
+ curr[1] + (next[1] - curr[1]) * t
+ ]);
+ }
+ } else if (!currIn && nextIn) {
+ // Entering half-plane: add intersection then next
+ const t = currVal / (currVal - nextVal);
+ if (t >= 0 && t <= 1) {
+ newPoly.push([
+ curr[0] + (next[0] - curr[0]) * t,
+ curr[1] + (next[1] - curr[1]) * t
+ ]);
+ }
+ newPoly.push(next);
+ }
+ }
+ return newPoly;
+}
+
+// Compute Voronoi cell for point at index
+function computeCell(index, allPoints) {
+ const bounds = [[0, 0], [width, 0], [width, height], [0, height]];
+ let cell = bounds;
+ const p1 = allPoints[index];
+
+ for (let j = 0; j < allPoints.length; j++) {
+ if (index === j) continue;
+ cell = clipPolygon(cell, p1, allPoints[j]);
+ if (cell.length === 0) break;
+ }
+ return cell;
+}
+
+// Calculate approximate max radius from point to cell vertices
+function getCellRadius(cell, cx, cy) {
+ let maxDist = 0;
+ for (const v of cell) {
+ const dx = v[0] - cx;
+ const dy = v[1] - cy;
+ maxDist = Math.max(maxDist, Math.sqrt(dx*dx + dy*dy));
+ }
+ return maxDist || 300;
+}
+
+function render() {
+ ctx.fillStyle = '#050505';
+ ctx.fillRect(0, 0, width, height);
+
+ if (points.length === 0) {
+ document.getElementById('count').textContent = '0';
+ return;
+ }
+
+ // Animation progress for newest point (0 to 1)
+ let growth = 1;
+ if (animating && points.length > 0) {
+ const newest = points[points.length - 1];
+ if (newest.born >= 0) {
+ const age = Date.now() - newest.born;
+ growth = Math.min(1, age / (ANIMATION_DURATION * 16.67)); // 60fps approx
+ if (growth >= 1) animating = false;
+ }
+ }
+
+ // Draw Voronoi cells
+ for (let i = 0; i < points.length; i++) {
+ const cell = computeCell(i, points);
+ if (cell.length < 3) continue;
+
+ const p = points[i];
+
+ // Animation mask for newest cell
+ if (i === points.length - 1 && growth < 1) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, getCellRadius(cell, p.x, p.y) * easeOutBack(growth), 0, Math.PI * 2);
+ ctx.clip();
+ }
+
+ // Draw cell
+ ctx.beginPath();
+ ctx.moveTo(cell[0][0], cell[0][1]);
+ for (let j = 1; j < cell.length; j++) {
+ ctx.lineTo(cell[j][0], cell[j][1]);
+ }
+ ctx.closePath();
+
+ // Glass-like gradient fill
+ const angle = p.hue * Math.PI / 180;
+ const grad = ctx.createLinearGradient(
+ p.x - Math.cos(angle) * 100,
+ p.y - Math.sin(angle) * 100,
+ p.x + Math.cos(angle) * 100,
+ p.y + Math.sin(angle) * 100
+ );
+ grad.addColorStop(0, `hsla(${p.hue}, 80%, 60%, 0.4)`);
+ grad.addColorStop(0.5, `hsla(${p.hue + 30}, 70%, 50%, 0.3)`);
+ grad.addColorStop(1, `hsla(${p.hue + 60}, 60%, 40%, 0.5)`);
+
+ ctx.fillStyle = grad;
+ ctx.fill();
+
+ // Edge glow
+ ctx.strokeStyle = `hsla(${p.hue}, 80%, 80%, 0.3)`;
+ ctx.lineWidth = 1.5;
+ ctx.stroke();
+
+ // Inner highlight
+ ctx.strokeStyle = `hsla(${p.hue}, 100%, 90%, 0.1)`;
+ ctx.lineWidth = 0.5;
+ ctx.stroke();
+
+ if (i === points.length - 1 && growth < 1) {
+ ctx.restore();
+
+ // Draw shatter lines during animation
+ ctx.save();
+ ctx.strokeStyle = `hsla(${p.hue}, 100%, 90%, ${1 - growth})`;
+ ctx.lineWidth = 2;
+ ctx.beginPath();
+ for (let j = 0; j < cell.length; j++) {
+ const midX = (p.x + cell[j][0]) / 2;
+ const midY = (p.y + cell[j][1]) / 2;
+ ctx.moveTo(p.x, p.y);
+ ctx.lineTo(midX, midY);
+ }
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+
+ // Draw seed points
+ if (showPoints) {
+ for (const p of points) {
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, 4, 0, Math.PI * 2);
+ ctx.fillStyle = '#fff';
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, 8, 0, Math.PI * 2);
+ ctx.strokeStyle = 'rgba(255,255,255,0.3)';
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+ }
+
+ document.getElementById('count').textContent = points.length;
+
+ if (animating) {
+ requestAnimationFrame(render);
+ }
+}
+
+function easeOutBack(x) {
+ const c1 = 1.70158;
+ const c3 = c1 + 1;
+ return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
+}
+
+canvas.addEventListener('click', (e) => {
+ const rect = canvas.getBoundingClientRect();
+ const newPoint = {
+ x: e.clientX - rect.left,
+ y: e.clientY - rect.top,
+ hue: Math.random() * 360,
+ born: Date.now()
+ };
+ points.push(newPoint);
+ animating = true;
+ render();
+});
+
+document.getElementById('togglePoints').addEventListener('click', (e) => {
+ showPoints = !showPoints;
+ e.target.textContent = showPoints ? 'Hide Points' : 'Show Points';
+ if (!animating) render();
+});
+
+document.getElementById('reset').addEventListener('click', () => {
+ points = [];
+ animating = false;
+ init();
+});
+
+init();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/48026228790d/kimi.png b/data/artifacts/48026228790d/kimi.png
new file mode 100644
index 0000000..4bf8faf
Binary files /dev/null and b/data/artifacts/48026228790d/kimi.png differ
diff --git a/data/artifacts/52fc117697fd/gemma3-12b.html b/data/artifacts/52fc117697fd/gemma3-12b.html
new file mode 100644
index 0000000..50f0494
--- /dev/null
+++ b/data/artifacts/52fc117697fd/gemma3-12b.html
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fireworks!</title>
+<style>
+body { margin: 0; overflow: hidden; background-color: #000; }
+canvas { display: block; }
+#controls { position: absolute; top: 10px; left: 10px; color: white; font-family: sans-serif; }
+</style>
+</head>
+<body>
+
+<div id="controls">
+ Click to launch! <input type="checkbox" id="autoLaunch" onchange="toggleAutoLaunch()"> Auto Launch
+</div>
+
+<canvas id="fireworksCanvas"></canvas>
+
+<script>
+const canvas = document.getElementById('fireworksCanvas');
+const ctx = canvas.getContext('2d');
+
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+let fireworks = [];
+let autoLaunchInterval;
+
+function Particle(x, y, color, size, life) {
+ this.x = x;
+ this.y = y;
+ this.color = color;
+ this.size = size;
+ this.life = life;
+ this.gravity = 0.1;
+ this.velocity = { x: Math.random() * 2 - 1, y: Math.random() * -2 }; // Initial velocity
+}
+
+Particle.prototype.update = function() {
+ this.velocity.y += this.gravity;
+ this.x += this.velocity.x;
+ this.y += this.velocity.y;
+ this.size *= 0.95; // Fading size
+ this.life--;
+};
+
+Particle.prototype.draw = function() {
+ ctx.fillStyle = this.color;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ ctx.fill();
+};
+
+
+function FireworksShell(x, y) {
+ this.x = x;
+ this.y = y;
+ this.explosionSize = 100;
+ this.particles = [];
+
+ this.launch = function() {
+ let angle = Math.random() * Math.PI * 2;
+ let speed = Math.random() * 5 + 5; // Launch speed
+ let vx = Math.cos(angle) * speed;
+ let vy = Math.sin(angle) * speed;
+
+ for (let i = 0; i < 30; i++) {
+ let particleAngle = angle + (Math.random() - 0.5) * Math.PI / 4;
+ let particleSpeed = Math.random() * 2 + 2; // Particle speed
+ let particleColor = `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 1)`;
+ let particleSize = Math.random() * 4 + 2;
+
+ let particle = new Particle(this.x, this.y, particleColor, particleSize, 80);
+ particle.velocity.x = Math.cos(particleAngle) * particleSpeed;
+ particle.velocity.y = Math.sin(particleAngle) * particleSpeed;
+ this.particles.push(particle);
+ }
+ };
+}
+
+function launchFireworks() {
+ let shell = new FireworksShell(Math.random() * canvas.width, -20); // Launch from top
+ shell.launch();
+ fireworks.push(shell);
+}
+
+
+function toggleAutoLaunch() {
+ if (document.getElementById('autoLaunch').checked) {
+ autoLaunchInterval = setInterval(launchFireworks, 1000);
+ } else {
+ clearInterval(autoLaunchInterval);
+ }
+}
+
+function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = fireworks.length - 1; i >= 0; i--) {
+ let shell = fireworks[i];
+
+ if (shell.particles.length > 0) {
+ for (let j = shell.particles.length - 1; j >= 0; j--) {
+ let particle = shell.particles[j];
+ particle.update();
+ particle.draw();
+
+ if (particle.life <= 0 || particle.size <= 0) {
+ shell.particles.splice(j, 1);
+ }
+ }
+
+ if (shell.particles.length === 0) {
+ fireworks.splice(i, 1);
+ }
+ } else {
+ fireworks.splice(i, 1); // Remove empty shells
+ }
+ }
+
+
+ requestAnimationFrame(animate);
+}
+
+canvas.addEventListener('click', launchFireworks);
+
+toggleAutoLaunch(); // Initialize auto-launch based on checkbox state
+animate();
+
+
+window.addEventListener('resize', function() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+});
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/52fc117697fd/gemma3-12b.png b/data/artifacts/52fc117697fd/gemma3-12b.png
new file mode 100644
index 0000000..8aac4d6
Binary files /dev/null and b/data/artifacts/52fc117697fd/gemma3-12b.png differ
diff --git a/data/artifacts/52fc117697fd/gpt.html b/data/artifacts/52fc117697fd/gpt.html
new file mode 100644
index 0000000..8b8cd00
--- /dev/null
+++ b/data/artifacts/52fc117697fd/gpt.html
@@ -0,0 +1,321 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>Fireworks Sky</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ background: radial-gradient(circle at top, #111 0%, #000 60%, #000 100%);
+ overflow: hidden;
+ height: 100%;
+ color: #eee;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ user-select: none;
+ }
+ #ui {
+ position: fixed;
+ top: 10px;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ padding: 6px 12px;
+ background: rgba(0,0,0,0.45);
+ border-radius: 999px;
+ box-shadow: 0 0 12px rgba(0,0,0,0.8);
+ backdrop-filter: blur(6px);
+ z-index: 10;
+ font-size: 13px;
+ }
+ #ui button, #ui label {
+ cursor: pointer;
+ }
+ #autoToggle {
+ width: 40px;
+ height: 20px;
+ background: #333;
+ border-radius: 999px;
+ position: relative;
+ box-shadow: inset 0 0 4px rgba(0,0,0,0.7);
+ transition: background 0.2s;
+ }
+ #autoToggle::before {
+ content: "";
+ position: absolute;
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: #eee;
+ top: 1px;
+ left: 1px;
+ box-shadow: 0 0 6px rgba(0,0,0,0.9);
+ transition: transform 0.2s;
+ }
+ #autoToggle.on {
+ background: #0a6b2b;
+ }
+ #autoToggle.on::before {
+ transform: translateX(20px);
+ }
+ #hint {
+ margin-left: 10px;
+ opacity: 0.7;
+ }
+ canvas {
+ position: absolute;
+ inset: 0;
+ display: block;
+ }
+</style>
+</head>
+<body>
+<div id="ui">
+ <div id="autoToggle" title="Toggle auto-launch"></div>
+ <span id="statusText">Auto: Off</span>
+ <span id="hint">Click anywhere to launch fireworks</span>
+</div>
+<canvas id="c"></canvas>
+<script>
+(function() {
+ const canvas = document.getElementById('c');
+ const ctx = canvas.getContext('2d');
+ let w = canvas.width = window.innerWidth;
+ let h = canvas.height = window.innerHeight;
+
+ let shells = [];
+ let particles = [];
+ let lastTime = performance.now();
+ let auto = false;
+ let autoTimer = 0;
+
+ const GRAVITY = 0.08;
+ const AIR_RESIST = 0.995;
+ const PARTICLE_FRICTION = 0.985;
+ const PARTICLE_GRAVITY = 0.06;
+ const SHELL_BASE_SPEED = 7;
+ const SHELL_MIN_HEIGHT = 0.3;
+ const SHELL_MAX_HEIGHT = 0.75;
+
+ function rand(min, max) {
+ return Math.random() * (max - min) + min;
+ }
+
+ function resize() {
+ w = canvas.width = window.innerWidth;
+ h = canvas.height = window.innerHeight;
+ }
+ window.addEventListener('resize', resize);
+
+ class Shell {
+ constructor(x, targetY, color) {
+ this.x = x;
+ this.y = h + 5;
+ this.targetY = targetY;
+ this.color = color;
+ this.size = rand(3, 4.5);
+ const maxHeight = h * targetY;
+ const travel = this.y - maxHeight;
+ this.vy = -Math.sqrt(2 * GRAVITY * travel);
+ this.vx = rand(-1.2, 1.2);
+ this.dead = false;
+ this.trail = [];
+ }
+ update(dt) {
+ this.vy += GRAVITY;
+ this.x += this.vx;
+ this.y += this.vy;
+
+ this.trail.push({x: this.x, y: this.y});
+ if (this.trail.length > 8) this.trail.shift();
+
+ if (this.vy >= 0 || this.y <= h * this.targetY) {
+ this.explode();
+ this.dead = true;
+ }
+ if (this.y > h + 50 || this.x < -50 || this.x > w + 50) {
+ this.dead = true;
+ }
+ }
+ explode() {
+ const colors = [this.color, randomColor(), randomColor()];
+ const type = Math.random();
+ const count = type < 0.3 ? rand(40, 70) : type < 0.7 ? rand(80, 140) : rand(160, 220);
+ const speed = type < 0.4 ? rand(2.2, 3.1) : rand(2.8, 4.3);
+ const ring = type > 0.75;
+ for (let i = 0; i < count; i++) {
+ const angle = Math.random() * Math.PI * 2;
+ const mag = ring ? speed : speed * (0.4 + Math.random() * 0.8);
+ const vx = Math.cos(angle) * mag;
+ const vy = Math.sin(angle) * mag;
+ const col = colors[Math.floor(Math.random() * colors.length)];
+ particles.push(new Particle(this.x, this.y, vx, vy, col, ring));
+ }
+ }
+ draw(ctx) {
+ ctx.save();
+ ctx.lineWidth = 2;
+ for (let i = 0; i < this.trail.length - 1; i++) {
+ const p1 = this.trail[i];
+ const p2 = this.trail[i + 1];
+ const alpha = i / this.trail.length;
+ ctx.strokeStyle = `rgba(${this.color.r},${this.color.g},${this.color.b},${alpha * 0.8})`;
+ ctx.beginPath();
+ ctx.moveTo(p1.x, p1.y);
+ ctx.lineTo(p2.x, p2.y);
+ ctx.stroke();
+ }
+ ctx.fillStyle = `rgba(${this.color.r},${this.color.g},${this.color.b},1)`;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+ }
+
+ class Particle {
+ constructor(x, y, vx, vy, color, ring) {
+ this.x = x;
+ this.y = y;
+ this.vx = vx;
+ this.vy = vy;
+ this.color = color;
+ this.life = 1;
+ this.decay = rand(0.007, 0.025);
+ this.size = rand(1.1, 2.4);
+ this.ring = ring;
+ this.trail = [];
+ this.flicker = Math.random() < 0.5;
+ }
+ update(dt) {
+ this.vx *= PARTICLE_FRICTION;
+ this.vy *= PARTICLE_FRICTION;
+ this.vy += PARTICLE_GRAVITY;
+ this.x += this.vx;
+ this.y += this.vy;
+ this.life -= this.decay * dt * 60;
+
+ this.trail.push({x: this.x, y: this.y});
+ if (this.trail.length > 6) this.trail.shift();
+
+ if (this.life <= 0) {
+ this.dead = true;
+ }
+ }
+ draw(ctx) {
+ if (this.life <= 0) return;
+ const alpha = Math.max(this.life, 0);
+ ctx.save();
+
+ for (let i = 0; i < this.trail.length - 1; i++) {
+ const p1 = this.trail[i];
+ const p2 = this.trail[i + 1];
+ const tAlpha = (i / this.trail.length) * alpha * 0.5;
+ ctx.strokeStyle = `rgba(${this.color.r},${this.color.g},${this.color.b},${tAlpha})`;
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(p1.x, p1.y);
+ ctx.lineTo(p2.x, p2.y);
+ ctx.stroke();
+ }
+
+ const flicker = this.flicker ? (0.7 + Math.random() * 0.8) : 1;
+ const radius = this.size * flicker * (this.ring ? 1.2 : 1);
+ const grad = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, radius);
+ grad.addColorStop(0, `rgba(255,255,255,${alpha})`);
+ grad.addColorStop(0.3, `rgba(${this.color.r},${this.color.g},${this.color.b},${alpha})`);
+ grad.addColorStop(1, `rgba(${this.color.r},${this.color.g},${this.color.b},0)`);
+
+ ctx.fillStyle = grad;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, radius, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.restore();
+ }
+ }
+
+ function randomColor() {
+ const schemes = [
+ {r: 255, g: rand(100, 200), b: rand(0, 60)},
+ {r: rand(0, 80), g: rand(100, 220), b: 255},
+ {r: rand(180, 255), g: rand(0, 80), b: rand(180, 255)},
+ {r: 255, g: 255, b: rand(100, 200)},
+ {r: rand(100, 255), g: rand(160, 255), b: rand(0, 40)},
+ {r: rand(150, 255), g: rand(0, 40), b: rand(0, 40)}
+ ];
+ return schemes[Math.floor(Math.random() * schemes.length)];
+ }
+
+ function launchShell(x) {
+ const color = randomColor();
+ const targetY = rand(SHELL_MIN_HEIGHT, SHELL_MAX_HEIGHT);
+ shells.push(new Shell(x, targetY, color));
+ }
+
+ function autoLaunch(dt) {
+ autoTimer -= dt;
+ if (autoTimer <= 0) {
+ const x = rand(w * 0.1, w * 0.9);
+ launchShell(x);
+ autoTimer = rand(0.2, 0.7);
+ }
+ }
+
+ function loop(now) {
+ const dt = Math.min((now - lastTime) / 16.67, 2);
+ lastTime = now;
+
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.fillStyle = 'rgba(0,0,0,0.18)';
+ ctx.fillRect(0, 0, w, h);
+
+ ctx.globalCompositeOperation = 'lighter';
+
+ if (auto) autoLaunch(dt);
+
+ shells.forEach(s => s.update(dt));
+ shells = shells.filter(s => !s.dead);
+
+ particles.forEach(p => p.update(dt));
+ particles = particles.filter(p => !p.dead);
+
+ shells.forEach(s => s.draw(ctx));
+ particles.forEach(p => p.draw(ctx));
+
+ requestAnimationFrame(loop);
+ }
+
+ canvas.addEventListener('click', (e) => {
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ launchShell(x);
+ });
+
+ const autoToggle = document.getElementById('autoToggle');
+ const statusText = document.getElementById('statusText');
+
+ function setAuto(state) {
+ auto = state;
+ if (auto) {
+ autoTimer = 0.1;
+ autoToggle.classList.add('on');
+ statusText.textContent = 'Auto: On';
+ } else {
+ autoToggle.classList.remove('on');
+ statusText.textContent = 'Auto: Off';
+ }
+ }
+
+ autoToggle.addEventListener('click', () => setAuto(!auto));
+
+ // Start
+ resize();
+ setAuto(true);
+ requestAnimationFrame(loop);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/52fc117697fd/gpt.png b/data/artifacts/52fc117697fd/gpt.png
new file mode 100644
index 0000000..c4f5ee7
Binary files /dev/null and b/data/artifacts/52fc117697fd/gpt.png differ
diff --git a/data/artifacts/52fc117697fd/grok.html b/data/artifacts/52fc117697fd/grok.html
new file mode 100644
index 0000000..3fe1bdf
--- /dev/null
+++ b/data/artifacts/52fc117697fd/grok.html
@@ -0,0 +1,250 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Fireworks</title>
+<style>
+*{margin:0;padding:0;box-sizing:border-box}
+html,body{width:100%;height:100%;overflow:hidden;background:#000}
+canvas{display:block;width:100%;height:100%;cursor:crosshair}
+#ui{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:10;display:flex;gap:12px;align-items:center;font-family:system-ui,-apple-system,sans-serif}
+button{background:rgba(255,255,255,0.12);border:1px solid rgba(255,255,255,0.25);color:#fff;padding:10px 18px;border-radius:8px;cursor:pointer;font-size:14px;backdrop-filter:blur(8px);transition:background 0.2s}
+button:hover{background:rgba(255,255,255,0.22)}
+button.active{background:rgba(255,180,50,0.35);border-color:rgba(255,200,80,0.6)}
+#hint{position:fixed;top:16px;left:50%;transform:translateX(-50%);color:rgba(255,255,255,0.45);font-family:system-ui,-apple-system,sans-serif;font-size:13px;pointer-events:none;letter-spacing:0.02em}
+</style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<div id="hint">Click to launch · enjoy the show</div>
+<div id="ui">
+<button id="autoBtn" class="active">Auto: ON</button>
+<button id="clearBtn">Clear</button>
+</div>
+<script>
+(function(){
+const canvas=document.getElementById('c');
+const ctx=canvas.getContext('2d');
+let W,H,dpr;
+const rockets=[];
+const particles=[];
+let autoLaunch=true;
+let lastAuto=0;
+const AUTO_MS=900;
+
+function resize(){
+ dpr=Math.min(window.devicePixelRatio||1,2);
+ 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();
+
+function rand(a,b){return a+Math.random()*(b-a)}
+function pick(arr){return arr[Math.floor(Math.random()*arr.length)]}
+function hsl(h,s,l,a){return `hsla(${h},${s}%,${l}%,${a})`}
+
+const PALETTES=[
+ [0,15,30],
+ [190,210,230],
+ [40,50,55],
+ [280,300,320],
+ [120,140,160],
+ [330,350,10],
+ [45,55,200],
+];
+
+class Rocket{
+ constructor(x,y,tx,ty){
+ this.x=x;this.y=y;
+ this.tx=tx;this.ty=ty;
+ const dx=tx-x,dy=ty-y;
+ const dist=Math.sqrt(dx*dx+dy*dy);
+ const speed=rand(8,12);
+ this.vx=(dx/dist)*speed;
+ this.vy=(dy/dist)*speed;
+ this.hue=rand(0,360);
+ this.trail=[];
+ this.alive=true;
+ this.history=8;
+ }
+ update(){
+ this.trail.push({x:this.x,y:this.y});
+ if(this.trail.length>this.history)this.trail.shift();
+ this.x+=this.vx;
+ this.y+=this.vy;
+ this.vy+=0.04;
+ const dx=this.tx-this.x,dy=this.ty-this.y;
+ if(dy>0||(dx*dx+dy*dy)<80||this.y<this.ty){
+ this.alive=false;
+ explode(this.x,this.y,this.hue);
+ }
+ }
+ draw(){
+ for(let i=0;i<this.trail.length;i++){
+ const t=this.trail[i];
+ const a=i/this.trail.length*0.6;
+ ctx.beginPath();
+ ctx.arc(t.x,t.y,1.5,0,Math.PI*2);
+ ctx.fillStyle=hsl(this.hue,100,70,a);
+ ctx.fill();
+ }
+ ctx.beginPath();
+ ctx.arc(this.x,this.y,2.5,0,Math.PI*2);
+ ctx.fillStyle=hsl(this.hue,100,85,1);
+ ctx.fill();
+ }
+}
+
+class Particle{
+ constructor(x,y,hue,speed,type){
+ this.x=x;this.y=y;
+ const angle=rand(0,Math.PI*2);
+ const sp=speed*rand(0.35,1.05);
+ this.vx=Math.cos(angle)*sp;
+ this.vy=Math.sin(angle)*sp;
+ this.hue=hue+rand(-18,18);
+ this.life=1;
+ this.decay=rand(0.008,0.018);
+ this.size=rand(1.2,2.8);
+ this.gravity=rand(0.02,0.055);
+ this.friction=0.985;
+ this.type=type||0;
+ this.spark=Math.random()>0.7;
+ this.twinkle=Math.random()>0.5;
+ }
+ update(){
+ this.vx*=this.friction;
+ this.vy*=this.friction;
+ this.vy+=this.gravity;
+ this.x+=this.vx;
+ this.y+=this.vy;
+ this.life-=this.decay;
+ if(this.twinkle&&Math.random()>0.92)this.life-=0.03;
+ }
+ draw(){
+ if(this.life<=0)return;
+ const a=Math.max(0,this.life);
+ const s=this.size*(0.4+a*0.6);
+ ctx.beginPath();
+ ctx.arc(this.x,this.y,s,0,Math.PI*2);
+ ctx.fillStyle=hsl(this.hue,100,this.spark?80:60,a);
+ ctx.fill();
+ if(a>0.5){
+ ctx.beginPath();
+ ctx.arc(this.x,this.y,s*0.4,0,Math.PI*2);
+ ctx.fillStyle=hsl(this.hue,80,95,a*0.9);
+ ctx.fill();
+ }
+ }
+}
+
+function explode(x,y,baseHue){
+ const palette=pick(PALETTES);
+ const count=Math.floor(rand(60,120));
+ const style=Math.floor(Math.random()*4);
+ for(let i=0;i<count;i++){
+ let hue;
+ if(style===0)hue=baseHue;
+ else if(style===1)hue=pick(palette);
+ else if(style===2)hue=baseHue+(i%2===0?0:180);
+ else hue=rand(0,360);
+ const speed=style===3?rand(2,14):rand(3,11);
+ particles.push(new Particle(x,y,hue,speed,style));
+ }
+ // secondary sparkle ring
+ if(Math.random()>0.4){
+ const ring=Math.floor(rand(20,40));
+ for(let i=0;i<ring;i++){
+ const p=new Particle(x,y,baseHue+rand(-10,10),rand(1.5,4),0);
+ p.decay=rand(0.015,0.03);
+ p.size=rand(0.8,1.6);
+ p.gravity=0.015;
+ particles.push(p);
+ }
+ }
+}
+
+function launch(tx,ty){
+ const x=rand(W*0.2,W*0.8);
+ const y=H+10;
+ const targetX=tx!=null?tx:rand(W*0.15,W*0.85);
+ const targetY=ty!=null?ty:rand(H*0.1,H*0.45);
+ rockets.push(new Rocket(x,y,targetX,targetY));
+}
+
+canvas.addEventListener('click',e=>{
+ const rect=canvas.getBoundingClientRect();
+ launch(e.clientX-rect.left,e.clientY-rect.top);
+});
+canvas.addEventListener('touchstart',e=>{
+ e.preventDefault();
+ const t=e.touches[0];
+ const rect=canvas.getBoundingClientRect();
+ launch(t.clientX-rect.left,t.clientY-rect.top);
+},{passive:false});
+
+document.getElementById('autoBtn').addEventListener('click',function(){
+ autoLaunch=!autoLaunch;
+ this.textContent=autoLaunch?'Auto: ON':'Auto: OFF';
+ this.classList.toggle('active',autoLaunch);
+});
+document.getElementById('clearBtn').addEventListener('click',()=>{
+ rockets.length=0;
+ particles.length=0;
+ ctx.fillStyle='#000';
+ ctx.fillRect(0,0,W,H);
+});
+
+let last=performance.now();
+function frame(now){
+ const dt=Math.min(50,now-last);
+ last=now;
+
+ // trail fade — dark sky with subtle trails
+ ctx.fillStyle='rgba(0,0,4,0.22)';
+ ctx.fillRect(0,0,W,H);
+
+ // faint stars
+ if(Math.random()>0.97){
+ ctx.fillStyle=`rgba(255,255,255,${rand(0.05,0.2)})`;
+ ctx.fillRect(rand(0,W),rand(0,H*0.6),1,1);
+ }
+
+ if(autoLaunch&&now-lastAuto>AUTO_MS){
+ lastAuto=now;
+ if(rockets.length<6)launch();
+ if(Math.random()>0.6)setTimeout(()=>launch(),rand(100,400));
+ }
+
+ for(let i=rockets.length-1;i>=0;i--){
+ const r=rockets[i];
+ r.update();
+ r.draw();
+ if(!r.alive)rockets.splice(i,1);
+ }
+
+ for(let i=particles.length-1;i>=0;i--){
+ const p=particles[i];
+ p.update();
+ p.draw();
+ if(p.life<=0)particles.splice(i,1);
+ }
+
+ requestAnimationFrame(frame);
+}
+
+// initial black
+ctx.fillStyle='#000';
+ctx.fillRect(0,0,W,H);
+// seed a few
+for(let i=0;i<3;i++)setTimeout(()=>launch(),i*400);
+requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/52fc117697fd/grok.png b/data/artifacts/52fc117697fd/grok.png
new file mode 100644
index 0000000..92564c7
Binary files /dev/null and b/data/artifacts/52fc117697fd/grok.png differ
diff --git a/data/artifacts/52fc117697fd/hermes3-8b.html b/data/artifacts/52fc117697fd/hermes3-8b.html
new file mode 100644
index 0000000..c9cebaa
--- /dev/null
+++ b/data/artifacts/52fc117697fd/hermes3-8b.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Fireworks Display</title>
+ <style>
+ body {
+ background-color: #000;
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
+ cursor: crosshair;
+ }
+
+ canvas {
+ display: block;
+ background-color: #fff;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="fireworks"></canvas>
+
+ <script>
+ const canvas = document.getElementById('fireworks');
+ let ctx;
+ let gravity = 0.2;
+ let friction = 0.99;
+ let particles = [];
+ let shellCount;
+
+ function init() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ ctx = canvas.getContext('2d');
+
+ document.addEventListener('click', launchShells, false);
+ setInterval(draw, 10);
+ }
+
+ function getRandomColor() {
+ const letters = '0123456789ABCDEF';
+ let color = '#';
+ for (let i = 0; i < 6; i++) {
+ color += letters[Math.floor(Math.random() * 16)];
+ }
+ return color;
+ }
+
+ function Particle(x, y) {
+ this.x = x;
+ this.y = y;
+ this.size = Math.random() * 5 + 1;
+ this.color = getRandomColor();
+ this.speedX = (Math.random() * 2 - 1) * 3;
+ this.speedY = (Math.random() * 2 - 1) * 3;
+ }
+
+ Particle.prototype.draw = function () {
+ ctx.fillStyle = this.color;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ ctx.fill();
+ };
+
+ Particle.prototype.update = function () {
+ this.speedY += gravity;
+ this.x -= this.speedX;
+ this.y -= this.speedY;
+
+ if (this.y > canvas.height || this.x < 0 || this.y < 0) {
+ return true;
+ }
+
+ return false;
+ };
+
+ function createShell(x, y, count) {
+ let i = -1;
+ shellCount = count;
+ while (++i < count) {
+ particles.push(new Particle(x, y));
+ }
+ }
+
+ function launchShells(event) {
+ if (shellCount !== undefined) return;
+
+ const rect = canvas.getBoundingClientRect();
+ const mouseX = event.clientX - rect.left;
+ const mouseY = event.clientY - rect.top;
+
+ createShell(mouseX, mouseY, Math.random() * 5 + 3);
+ }
+
+ function draw() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = particles.length - 1; i > -1; i--) {
+ let p = particles[i];
+
+ if (p.update()) {
+ particles.splice(i, 1);
+ continue;
+ }
+
+ p.draw();
+ }
+ }
+
+ init();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/52fc117697fd/hermes3-8b.png b/data/artifacts/52fc117697fd/hermes3-8b.png
new file mode 100644
index 0000000..6f205e4
Binary files /dev/null and b/data/artifacts/52fc117697fd/hermes3-8b.png differ
diff --git a/data/artifacts/52fc117697fd/hf-qwen-coder-32b.html b/data/artifacts/52fc117697fd/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..0a48cad
--- /dev/null
+++ b/data/artifacts/52fc117697fd/hf-qwen-coder-32b.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Fireworks Display</title>
+<style>
+body, html { margin: 0; padding: 0; overflow: hidden; background: black; }
+canvas { display: block; }
+button { position: absolute; top: 10px; left: 10px; color: white; background: rgba(0, 0, 0, 0.5); border: none; padding: 8px; cursor: pointer; z-index: 10; }
+</style>
+</head>
+<body>
+<canvas id="fireworks"></canvas>
+<button onclick="toggleAutoLaunch()">Toggle Auto Launch</button>
+<script>
+const canvas = document.getElementById('fireworks');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+class Particle {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.size = Math.random() * 5 + 1;
+ this.speedX = (Math.random() - 0.5) * 3;
+ this.speedY = Math.random() * 7 + 2;
+ this.color = `hsl(${Math.random() * 360}, 100%, 50%)`;
+ this.alpha = 1;
+ }
+ update() {
+ this.x += this.speedX;
+ this.y -= this.speedY;
+ this.size *= 0.97;
+ this.alpha -= 0.01;
+ if (this.alpha <= 0) {
+ this.alpha = 0;
+ }
+ }
+ draw() {
+ ctx.globalAlpha = this.alpha;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ }
+}
+
+let particlesArray = [];
+function createFirework(x, y) {
+ for (let i = 0; i < 150; i++) {
+ particlesArray.push(new Particle(x, y));
+ }
+}
+
+function handleParticles() {
+ for (let i = 0; i < particlesArray.length; i++) {
+ particlesArray[i].update();
+ particlesArray[i].draw();
+ if (particlesArray[i].alpha <= 0) {
+ particlesArray.splice(i, 1);
+ i--;
+ }
+ }
+}
+
+function animate() {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ handleParticles();
+ if (autoLaunch) requestAnimationFrame(autoFirework);
+ requestAnimationFrame(animate);
+}
+let autoLaunch = false;
+function toggleAutoLaunch() {
+ autoLaunch = !autoLaunch;
+}
+
+canvas.addEventListener('click', function(event) {
+ const x = event.clientX;
+ const y = event.clientY;
+ createFirework(x, y);
+});
+
+function autoFirework() {
+ const x = Math.random() * canvas.width;
+ const y = canvas.height;
+ createFirework(x, y);
+}
+
+animate();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/52fc117697fd/hf-qwen-coder-32b.png b/data/artifacts/52fc117697fd/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..f0727ea
Binary files /dev/null and b/data/artifacts/52fc117697fd/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/52fc117697fd/kimi.html b/data/artifacts/52fc117697fd/kimi.html
new file mode 100644
index 0000000..ca21cfc
--- /dev/null
+++ b/data/artifacts/52fc117697fd/kimi.html
@@ -0,0 +1,241 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Interactive Fireworks</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #000;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ }
+ canvas {
+ display: block;
+ cursor: crosshair;
+ }
+ #controls {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ color: rgba(255, 255, 255, 0.9);
+ user-select: none;
+ pointer-events: none;
+ }
+ #autoBtn {
+ pointer-events: auto;
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ color: white;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 14px;
+ backdrop-filter: blur(4px);
+ transition: all 0.3s;
+ }
+ #autoBtn:hover {
+ background: rgba(255, 255, 255, 0.2);
+ border-color: rgba(255, 255, 255, 0.5);
+ }
+ #autoBtn.active {
+ background: rgba(100, 255, 100, 0.2);
+ border-color: rgba(100, 255, 100, 0.5);
+ }
+ .hint {
+ font-size: 12px;
+ opacity: 0.6;
+ margin-top: 8px;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="canvas"></canvas>
+ <div id="controls">
+ <button id="autoBtn">Auto Launch: OFF</button>
+ <div class="hint">Click anywhere to launch</div>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const autoBtn = document.getElementById('autoBtn');
+
+ let width, height;
+ let fireworks = [];
+ let particles = [];
+ let autoLaunch = false;
+ let autoInterval;
+
+ function resize() {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ }
+
+ window.addEventListener('resize', resize);
+ resize();
+
+ function randomColor() {
+ const hue = Math.random() * 360;
+ return `hsl(${hue}, 100%, 60%)`;
+ }
+
+ class Firework {
+ constructor(startX, targetY) {
+ this.x = startX;
+ this.y = height;
+ this.targetY = targetY;
+ this.color = randomColor();
+ this.speed = 6 + Math.random() * 4;
+ const angle = -Math.PI / 2 + (Math.random() - 0.5) * 0.3;
+ this.vx = Math.cos(angle) * this.speed;
+ this.vy = Math.sin(angle) * this.speed;
+ this.trail = [];
+ this.dead = false;
+ }
+
+ update() {
+ this.trail.push({x: this.x, y: this.y});
+ if (this.trail.length > 5) this.trail.shift();
+
+ this.x += this.vx;
+ this.y += this.vy;
+ this.vy += 0.15; // gravity
+
+ // Check if reached target height or apex
+ if (this.y <= this.targetY || this.vy >= 0) {
+ this.dead = true;
+ this.explode();
+ }
+ }
+
+ explode() {
+ const count = 40 + Math.random() * 40;
+ for (let i = 0; i < count; i++) {
+ particles.push(new Particle(this.x, this.y, this.color));
+ }
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.strokeStyle = this.color;
+ ctx.lineWidth = 2;
+ for (let i = 0; i < this.trail.length - 1; i++) {
+ ctx.moveTo(this.trail[i].x, this.trail[i].y);
+ ctx.lineTo(this.trail[i+1].x, this.trail[i+1].y);
+ }
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.fillStyle = '#fff';
+ ctx.arc(this.x, this.y, 2, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ class Particle {
+ constructor(x, y, color) {
+ this.x = x;
+ this.y = y;
+ this.color = color;
+ const angle = Math.random() * Math.PI * 2;
+ const speed = Math.random() * 4 + 1;
+ this.vx = Math.cos(angle) * speed;
+ this.vy = Math.sin(angle) * speed;
+ this.alpha = 1;
+ this.decay = 0.01 + Math.random() * 0.02;
+ this.gravity = 0.08;
+ this.flicker = Math.random() * 0.5 + 0.5;
+ }
+
+ update() {
+ this.vx *= 0.96; // air resistance
+ this.vy *= 0.96;
+ this.vy += this.gravity;
+ this.x += this.vx;
+ this.y += this.vy;
+ this.alpha -= this.decay;
+
+ // Flicker effect
+ if (Math.random() > 0.8) {
+ this.flicker = Math.random() * 0.5 + 0.5;
+ }
+ }
+
+ draw() {
+ const alpha = this.alpha * this.flicker;
+ if (alpha <= 0) return;
+
+ ctx.globalAlpha = alpha;
+ ctx.fillStyle = this.color;
+ ctx.fillRect(this.x - 1, this.y - 1, 3, 3);
+ ctx.globalAlpha = 1;
+ }
+ }
+
+ function loop() {
+ // Trail effect - fade out previous frames
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';
+ ctx.fillRect(0, 0, width, height);
+
+ // Update and draw fireworks
+ for (let i = fireworks.length - 1; i >= 0; i--) {
+ fireworks[i].update();
+ fireworks[i].draw();
+ if (fireworks[i].dead) {
+ fireworks.splice(i, 1);
+ }
+ }
+
+ // Update and draw particles
+ for (let i = particles.length - 1; i >= 0; i--) {
+ particles[i].update();
+ particles[i].draw();
+ if (particles[i].alpha <= 0) {
+ particles.splice(i, 1);
+ }
+ }
+
+ requestAnimationFrame(loop);
+ }
+
+ function launch(x, y) {
+ fireworks.push(new Firework(x, y));
+ }
+
+ // Click to launch
+ canvas.addEventListener('mousedown', (e) => {
+ launch(e.clientX, e.clientY);
+ });
+
+ canvas.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ for (let i = 0; i < e.touches.length; i++) {
+ launch(e.touches[i].clientX, e.touches[i].clientY);
+ }
+ }, {passive: false});
+
+ // Auto launch toggle
+ autoBtn.addEventListener('click', () => {
+ autoLaunch = !autoLaunch;
+ autoBtn.textContent = `Auto Launch: ${autoLaunch ? 'ON' : 'OFF'}`;
+ autoBtn.classList.toggle('active', autoLaunch);
+
+ if (autoLaunch) {
+ autoInterval = setInterval(() => {
+ const x = Math.random() * width * 0.8 + width * 0.1;
+ const y = Math.random() * height * 0.4 + height * 0.1;
+ launch(x, y);
+ }, 600);
+ } else {
+ clearInterval(autoInterval);
+ }
+ });
+
+ // Start animation
+ loop();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/52fc117697fd/kimi.png b/data/artifacts/52fc117697fd/kimi.png
new file mode 100644
index 0000000..4c92c9d
Binary files /dev/null and b/data/artifacts/52fc117697fd/kimi.png differ
diff --git a/data/artifacts/52fc117697fd/qwen25-7b.html b/data/artifacts/52fc117697fd/qwen25-7b.html
new file mode 100644
index 0000000..6ca1bdf
--- /dev/null
+++ b/data/artifacts/52fc117697fd/qwen25-7b.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Interactive Fireworks Display</title>
+ <style>
+ body { margin: 0; overflow: hidden; background: #111; }
+ canvas { display: block; }
+ </style>
+</head>
+<body>
+<canvas id="fireworks"></canvas>
+<script>
+const canvas = document.getElementById('fireworks');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+let autoLaunch = true;
+let gravity = 0.5, fadeSpeed = 0.98;
+let particles = [];
+
+class Particle {
+ constructor(x, y, color) {
+ this.x = x;
+ this.y = y;
+ this.color = color;
+ this.size = Math.random() * 2 + 1;
+ this.speedY = -Math.random() * 5 - 2;
+ this.speedX = (Math.random() - 0.5) * 4;
+ }
+
+ update() {
+ if(this.y < canvas.height) {
+ this.y += this.speedY;
+ this.speedY += gravity;
+ this.size *= fadeSpeed;
+ } else {
+ particles.splice(particles.indexOf(this), 1);
+ }
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ }
+}
+
+function launchParticle(x, y) {
+ particles.push(new Particle(x + 3, y - 5, `hsl(${Math.random() * 360},100%,75%)`));
+}
+
+canvas.addEventListener('click', () => {
+ if(autoLaunch) launchParticle(canvas.width / 2, canvas.height);
+});
+
+function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ particles.forEach(particle => {
+ particle.update();
+ particle.draw();
+ });
+ requestAnimationFrame(animate);
+}
+
+animate();
+
+function toggleAutoLaunch() {
+ autoLaunch = !autoLaunch;
+ console.log(`Auto-launch is ${autoLaunch ? 'on' : 'off'}`);
+}
+document.addEventListener('keydown', (e) => e.key === 'Enter' && toggleAutoLaunch());
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/52fc117697fd/qwen25-7b.png b/data/artifacts/52fc117697fd/qwen25-7b.png
new file mode 100644
index 0000000..71d328a
Binary files /dev/null and b/data/artifacts/52fc117697fd/qwen25-7b.png differ
diff --git a/data/artifacts/52fc117697fd/qwen3-14b.html b/data/artifacts/52fc117697fd/qwen3-14b.html
new file mode 100644
index 0000000..5382497
--- /dev/null
+++ b/data/artifacts/52fc117697fd/qwen3-14b.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Fireworks Display</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #000;
+ color: #fff;
+ font-family: sans-serif;
+ }
+ canvas {
+ display: block;
+ }
+ #controls {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ z-index: 10;
+ background: rgba(0,0,0,0.5);
+ padding: 10px;
+ border-radius: 5px;
+ }
+ button {
+ background: #333;
+ color: #fff;
+ border: none;
+ padding: 8px 12px;
+ margin-right: 8px;
+ cursor: pointer;
+ border-radius: 4px;
+ }
+ button:hover {
+ background: #555;
+ }
+ </style>
+</head>
+<body>
+ <div id="controls">
+ <button id="launch">Launch Fireworks</button>
+ <button id="toggleAuto">Auto Launch: Off</button>
+ </div>
+ <canvas id="fireworks"></canvas>
+ <script>
+ const canvas = document.getElementById('fireworks');
+ const ctx = canvas.getContext('2d');
+ let width, height;
+ let autoLaunch = false;
+ let launchInterval;
+ let launchButton = document.getElementById('launch');
+ let toggleAutoButton = document.getElementById('toggleAuto');
+
+ function resize() {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ }
+ window.addEventListener('resize', resize);
+ resize();
+
+ const fireworks = [];
+
+ class Shell {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.angle = Math.random() * Math.PI * 2;
+ this.speed = Math.random() * 3 + 2;
+ this.particles = [];
+ this.exploded = false;
+ }
+
+ update() {
+ if (!this.exploded) {
+ this.x += Math.cos(this.angle) * this.speed;
+ this.y += Math.sin(this.angle) * this.speed;
+ if (this.y > height) {
+ this.exploded = true;
+ this.explode();
+ }
+ } else {
+ for (let p of this.particles) {
+ p.update();
+ }
+ }
+ }
+
+ explode() {
+ for (let i = 0; i < 50; i++) {
+ this.particles.push(new Particle(this.x, this.y));
+ }
+ }
+
+ draw() {
+ if (!this.exploded) {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, 5, 0, Math.PI * 2);
+ ctx.fillStyle = 'white';
+ ctx.fill();
+ } else {
+ for (let p of this.particles) {
+ p.draw();
+ }
+ }
+ }
+ }
+
+ class Particle {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.vx = (Math.random() - 0.5) * 4;
+ this.vy = (Math.random() - 0.5) * 4 - 2;
+ this.gravity = 0.05;
+ this.life = 1;
+ this.color = `hsl(${Math.random() * 360}, 100%, 50%)`;
+ }
+
+ update() {
+ this.vy += this.gravity;
+ this.x += this.vx;
+ this.y += this.vy;
+ this.life -= 0.01;
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, 2 + this.life * 4, 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.globalAlpha = this.life;
+ ctx.fill();
+ ctx.globalAlpha = 1;
+ }
+ }
+
+ function animate() {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
+ ctx.fillRect(0, 0, width, height);
+ for (let f of fireworks) {
+ f.update();
+ f.draw();
+ }
+ requestAnimationFrame(animate);
+ }
+
+ function launchFireworks() {
+ const x = Math.random() * width;
+ const y = height * 0.8;
+ fireworks.push(new Shell(x, y));
+ }
+
+ launchButton.addEventListener('click', () => {
+ launchFireworks();
+ });
+
+ toggleAutoButton.addEventListener('click', () => {
+ autoLaunch = !autoLaunch;
+ if (autoLaunch) {
+ toggleAutoButton.textContent = 'Auto Launch: On';
+ launchInterval = setInterval(launchFireworks, 1500);
+ } else {
+ toggleAutoButton.textContent = 'Auto Launch: Off';
+ clearInterval(launchInterval);
+ }
+ });
+
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/52fc117697fd/qwen3-14b.png b/data/artifacts/52fc117697fd/qwen3-14b.png
new file mode 100644
index 0000000..2b7e260
Binary files /dev/null and b/data/artifacts/52fc117697fd/qwen3-14b.png differ
diff --git a/data/artifacts/673cf6b31c78/claude-code.html b/data/artifacts/673cf6b31c78/claude-code.html
new file mode 100644
index 0000000..7a452d7
--- /dev/null
+++ b/data/artifacts/673cf6b31c78/claude-code.html
@@ -0,0 +1,443 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>NEON SNAKE</title>
+<style>
+ :root{
+ --bg:#05060f;
+ --cyan:#00f0ff;
+ --pink:#ff2bd6;
+ --green:#39ff8e;
+ --dim:#8892b0;
+ }
+ *{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
+ html,body{height:100%}
+ body{
+ background:radial-gradient(1200px 800px at 50% 20%, #0b1030 0%, var(--bg) 60%);
+ color:#e6f1ff;
+ font-family:"Segoe UI", system-ui, -apple-system, Helvetica, Arial, sans-serif;
+ display:flex;align-items:center;justify-content:center;
+ overflow:hidden;
+ user-select:none;
+ }
+ #wrap{
+ display:flex;flex-direction:column;align-items:center;gap:14px;
+ padding:16px;
+ }
+ h1{
+ font-size:clamp(26px,5vw,40px);
+ font-weight:800;
+ letter-spacing:.35em;
+ text-indent:.35em;
+ color:#fff;
+ text-shadow:0 0 6px var(--cyan),0 0 18px var(--cyan),0 0 42px rgba(0,240,255,.6);
+ animation:titlePulse 2.6s ease-in-out infinite;
+ }
+ @keyframes titlePulse{
+ 0%,100%{text-shadow:0 0 6px var(--cyan),0 0 18px var(--cyan),0 0 42px rgba(0,240,255,.55)}
+ 50%{text-shadow:0 0 8px var(--pink),0 0 24px var(--pink),0 0 52px rgba(255,43,214,.55)}
+ }
+ #hud{
+ width:100%;max-width:520px;
+ display:flex;justify-content:space-between;align-items:center;
+ font-size:15px;letter-spacing:.12em;
+ }
+ .stat{color:var(--dim)}
+ .stat b{
+ color:var(--green);
+ font-size:20px;
+ text-shadow:0 0 8px rgba(57,255,142,.9);
+ font-variant-numeric:tabular-nums;
+ }
+ #soundBtn{
+ background:rgba(0,240,255,.06);
+ border:1px solid rgba(0,240,255,.4);
+ color:var(--cyan);
+ border-radius:8px;
+ padding:6px 12px;
+ font-size:14px;
+ cursor:pointer;
+ letter-spacing:.05em;
+ transition:all .2s;
+ }
+ #soundBtn:hover{background:rgba(0,240,255,.16);box-shadow:0 0 12px rgba(0,240,255,.5)}
+ #stage{position:relative}
+ canvas{
+ display:block;
+ border-radius:12px;
+ background:rgba(3,5,16,.9);
+ box-shadow:
+ 0 0 0 1px rgba(0,240,255,.35),
+ 0 0 22px rgba(0,240,255,.28),
+ inset 0 0 60px rgba(0,20,40,.8);
+ }
+ .overlay{
+ position:absolute;inset:0;
+ display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;
+ background:rgba(4,6,18,.72);
+ backdrop-filter:blur(4px);
+ border-radius:12px;
+ text-align:center;
+ opacity:0;pointer-events:none;
+ transition:opacity .25s ease;
+ }
+ .overlay.show{opacity:1;pointer-events:auto}
+ .overlay h2{
+ font-size:30px;letter-spacing:.25em;text-indent:.25em;font-weight:800;
+ }
+ .overlay.menu h2{color:#fff;text-shadow:0 0 10px var(--green),0 0 30px rgba(57,255,142,.7)}
+ .overlay.dead h2{color:#fff;text-shadow:0 0 10px var(--pink),0 0 30px rgba(255,43,214,.8)}
+ .overlay p{color:var(--dim);font-size:14px;line-height:1.7;letter-spacing:.06em}
+ .overlay .big{font-size:44px;color:#fff;font-weight:800;text-shadow:0 0 14px var(--cyan),0 0 36px rgba(0,240,255,.6)}
+ .btn{
+ background:transparent;
+ border:1px solid var(--cyan);
+ color:var(--cyan);
+ font-size:15px;letter-spacing:.2em;text-indent:.2em;
+ padding:12px 30px;border-radius:999px;cursor:pointer;
+ text-transform:uppercase;
+ transition:all .2s;
+ box-shadow:0 0 12px rgba(0,240,255,.25), inset 0 0 12px rgba(0,240,255,.08);
+ }
+ .btn:hover{
+ background:rgba(0,240,255,.12);
+ box-shadow:0 0 26px rgba(0,240,255,.65), inset 0 0 18px rgba(0,240,255,.2);
+ transform:translateY(-1px);
+ }
+ #hint{color:var(--dim);font-size:12.5px;letter-spacing:.14em;text-align:center}
+ kbd{
+ border:1px solid rgba(136,146,176,.5);border-radius:4px;
+ padding:1px 6px;font-family:inherit;font-size:11.5px;color:#cdd6f4;
+ }
+</style>
+</head>
+<body>
+<div id="wrap">
+ <h1>NEON SNAKE</h1>
+ <div id="hud">
+ <span class="stat">SCORE <b id="score">0</b></span>
+ <button id="soundBtn" title="Toggle sound">🔊 SOUND</button>
+ <span class="stat">BEST <b id="best">0</b></span>
+ </div>
+ <div id="stage">
+ <canvas id="game" width="480" height="480"></canvas>
+ <div class="overlay menu show" id="menu">
+ <h2>READY?</h2>
+ <p>Eat the orbs. Don't hit walls.<br>Don't bite yourself. Speed rises.</p>
+ <button class="btn" id="startBtn">Start</button>
+ </div>
+ <div class="overlay dead" id="dead">
+ <h2>GAME OVER</h2>
+ <div class="big" id="finalScore">0</div>
+ <p id="deadMsg"></p>
+ <button class="btn" id="restartBtn">Restart</button>
+ </div>
+ </div>
+ <div id="hint"><kbd>↑</kbd><kbd>↓</kbd><kbd>←</kbd><kbd>→</kbd> or <kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> to move · <kbd>P</kbd> pause · <kbd>Enter</kbd> start</div>
+</div>
+
+<script>
+(function(){
+ "use strict";
+ const COLS = 24, ROWS = 24, CELL = 20;
+ const W = COLS*CELL, H = ROWS*CELL;
+ const canvas = document.getElementById('game');
+ const ctx = canvas.getContext('2d');
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
+ canvas.width = W*dpr; canvas.height = H*dpr;
+ canvas.style.width = Math.min(W, window.innerWidth-40) + 'px';
+ canvas.style.height = canvas.style.width;
+ ctx.scale(dpr, dpr);
+
+ const scoreEl = document.getElementById('score');
+ const bestEl = document.getElementById('best');
+ const menuEl = document.getElementById('menu');
+ const deadEl = document.getElementById('dead');
+ const finalEl = document.getElementById('finalScore');
+ const deadMsg = document.getElementById('deadMsg');
+ const soundBtn = document.getElementById('soundBtn');
+
+ // ---------- sound (WebAudio, no assets) ----------
+ let soundOn = true, actx = null;
+ function audio(){ if(!actx){ try{ actx = new (window.AudioContext||window.webkitAudioContext)(); }catch(e){} } return actx; }
+ function blip(freq, dur, type, vol, slide){
+ if(!soundOn) return;
+ const a = audio(); if(!a) return;
+ if(a.state === 'suspended') a.resume();
+ const t = a.currentTime;
+ const o = a.createOscillator(), g = a.createGain();
+ o.type = type || 'square';
+ o.frequency.setValueAtTime(freq, t);
+ if(slide) o.frequency.exponentialRampToValueAtTime(Math.max(30, slide), t + dur);
+ g.gain.setValueAtTime(vol || .08, t);
+ g.gain.exponentialRampToValueAtTime(.0001, t + dur);
+ o.connect(g); g.connect(a.destination);
+ o.start(t); o.stop(t + dur + .02);
+ }
+ const sEat = () => { blip(520,.09,'square',.07,900); blip(1040,.12,'sine',.05); };
+ const sTurn = () => blip(220,.03,'triangle',.025);
+ const sDie = () => { blip(400,.5,'sawtooth',.09,60); blip(180,.7,'square',.06,40); };
+ const sStart = () => { blip(330,.1,'square',.06,660); setTimeout(()=>blip(660,.12,'square',.06,990),90); };
+ soundBtn.addEventListener('click', () => {
+ soundOn = !soundOn;
+ soundBtn.textContent = soundOn ? '🔊 SOUND' : '🔇 MUTED';
+ if(soundOn) blip(660,.06,'sine',.05);
+ });
+
+ // ---------- state ----------
+ let snake, prevSnake, dir, dirQueue, food, score, best, state, stepMs, acc, lastT;
+ let particles = [], shake = 0, foodPulse = 0, paused = false, deaths = 0;
+ best = +(localStorage.getItem('neonSnakeBest') || 0);
+ bestEl.textContent = best;
+
+ function reset(){
+ const cx = Math.floor(COLS/2), cy = Math.floor(ROWS/2);
+ snake = [{x:cx,y:cy},{x:cx-1,y:cy},{x:cx-2,y:cy},{x:cx-3,y:cy}];
+ prevSnake = snake.map(s=>({...s}));
+ dir = {x:1,y:0};
+ dirQueue = [];
+ score = 0; stepMs = 150; acc = 0; particles = []; shake = 0; paused = false;
+ scoreEl.textContent = '0';
+ placeFood();
+ }
+ function placeFood(){
+ let ok = false, f;
+ while(!ok){
+ f = {x:(Math.random()*COLS)|0, y:(Math.random()*ROWS)|0};
+ ok = !snake.some(s=>s.x===f.x && s.y===f.y);
+ }
+ food = f;
+ }
+
+ // ---------- input ----------
+ const keyMap = {
+ ArrowUp:{x:0,y:-1}, ArrowDown:{x:0,y:1}, ArrowLeft:{x:-1,y:0}, ArrowRight:{x:1,y:0},
+ w:{x:0,y:-1}, s:{x:0,y:1}, a:{x:-1,y:0}, d:{x:1,y:0},
+ W:{x:0,y:-1}, S:{x:0,y:1}, A:{x:-1,y:0}, D:{x:1,y:0}
+ };
+ window.addEventListener('keydown', e => {
+ if(e.key === 'Enter' || (e.key === ' ' && state !== 'play')){
+ e.preventDefault();
+ if(state === 'menu' || state === 'dead') start();
+ return;
+ }
+ if((e.key === 'p' || e.key === 'P' || e.key === ' ') && state === 'play'){
+ e.preventDefault(); paused = !paused; blip(300,.05,'sine',.04);
+ return;
+ }
+ const nd = keyMap[e.key];
+ if(!nd) return;
+ e.preventDefault();
+ if(state !== 'play') return;
+ const last = dirQueue.length ? dirQueue[dirQueue.length-1] : dir;
+ if(nd.x === -last.x && nd.y === -last.y) return; // no 180°
+ if(nd.x === last.x && nd.y === last.y) return; // no dup
+ if(dirQueue.length < 3){ dirQueue.push(nd); sTurn(); }
+ }, {passive:false});
+
+ // simple swipe support
+ let tStart = null;
+ canvas.addEventListener('touchstart', e => { tStart = e.touches[0]; }, {passive:true});
+ canvas.addEventListener('touchend', e => {
+ if(!tStart) return;
+ const dx = e.changedTouches[0].clientX - tStart.clientX;
+ const dy = e.changedTouches[0].clientY - tStart.clientY;
+ if(Math.abs(dx) < 18 && Math.abs(dy) < 18) return;
+ const nd = Math.abs(dx) > Math.abs(dy) ? {x:Math.sign(dx),y:0} : {x:0,y:Math.sign(dy)};
+ const last = dirQueue.length ? dirQueue[dirQueue.length-1] : dir;
+ if(!(nd.x === -last.x && nd.y === -last.y) && dirQueue.length < 3) dirQueue.push(nd);
+ tStart = null;
+ }, {passive:true});
+
+ document.getElementById('startBtn').addEventListener('click', start);
+ document.getElementById('restartBtn').addEventListener('click', start);
+
+ function start(){
+ reset();
+ state = 'play';
+ menuEl.classList.remove('show');
+ deadEl.classList.remove('show');
+ sStart();
+ }
+
+ // ---------- game step ----------
+ function step(){
+ prevSnake = snake.map(s=>({...s}));
+ if(dirQueue.length) dir = dirQueue.shift();
+ const head = {x:snake[0].x + dir.x, y:snake[0].y + dir.y};
+
+ // wall hit
+ if(head.x < 0 || head.y < 0 || head.x >= COLS || head.y >= ROWS){ die(); return; }
+ // self hit (tail cell vacates this step unless we eat, so exclude last segment)
+ const eating = head.x === food.x && head.y === food.y;
+ const body = eating ? snake : snake.slice(0, -1);
+ if(body.some(s=>s.x===head.x && s.y===head.y)){ die(); return; }
+
+ snake.unshift(head);
+ if(eating){
+ score++;
+ scoreEl.textContent = score;
+ stepMs = Math.max(62, 150 - score*3.2); // speed up
+ burst(food.x, food.y);
+ sEat();
+ placeFood();
+ prevSnake.unshift({...prevSnake[0]}); // keep interp arrays aligned
+ } else {
+ snake.pop();
+ }
+ }
+
+ function die(){
+ state = 'dead';
+ deaths++;
+ shake = 14;
+ sDie();
+ for(let i=0;i<40;i++) burstOne(snake[0].x + dir.x*.5, snake[0].y + dir.y*.5, true);
+ if(score > best){
+ best = score;
+ localStorage.setItem('neonSnakeBest', best);
+ bestEl.textContent = best;
+ deadMsg.textContent = '★ NEW BEST ★';
+ deadMsg.style.color = '#39ff8e';
+ } else {
+ deadMsg.textContent = 'best ' + best + ' · press Enter to retry';
+ deadMsg.style.color = '';
+ }
+ finalEl.textContent = score;
+ setTimeout(()=>deadEl.classList.add('show'), 350);
+ }
+
+ // ---------- particles ----------
+ function burstOne(gx, gy, red){
+ const a = Math.random()*Math.PI*2, sp = 40 + Math.random()*140;
+ particles.push({
+ x:(gx+.5)*CELL, y:(gy+.5)*CELL,
+ vx:Math.cos(a)*sp, vy:Math.sin(a)*sp,
+ life:1, decay:1.4 + Math.random()*1.6,
+ c: red ? (Math.random()<.5?'#ff2bd6':'#ff5f7a') : (Math.random()<.5?'#ff2bd6':'#00f0ff'),
+ r:1.5+Math.random()*2.5
+ });
+ }
+ function burst(gx, gy){ for(let i=0;i<22;i++) burstOne(gx, gy); }
+
+ // ---------- render ----------
+ function lerp(a,b,t){ return a + (b-a)*t; }
+
+ function drawGrid(){
+ ctx.strokeStyle = 'rgba(0,240,255,.05)';
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ for(let i=1;i<COLS;i++){ ctx.moveTo(i*CELL,0); ctx.lineTo(i*CELL,H); }
+ for(let j=1;j<ROWS;j++){ ctx.moveTo(0,j*CELL); ctx.lineTo(W,j*CELL); }
+ ctx.stroke();
+ }
+
+ function draw(t){
+ ctx.save();
+ ctx.clearRect(0,0,W,H);
+ if(shake > 0){
+ ctx.translate((Math.random()-.5)*shake, (Math.random()-.5)*shake);
+ shake *= .86; if(shake < .4) shake = 0;
+ }
+ drawGrid();
+
+ // food — pulsing orb
+ foodPulse += .09;
+ const fp = 1 + Math.sin(foodPulse)*.18;
+ const fx = (food.x+.5)*CELL, fy = (food.y+.5)*CELL;
+ ctx.save();
+ ctx.shadowColor = '#ff2bd6';
+ ctx.shadowBlur = 18 + Math.sin(foodPulse)*6;
+ ctx.fillStyle = '#ff2bd6';
+ ctx.beginPath(); ctx.arc(fx, fy, 6.5*fp, 0, Math.PI*2); ctx.fill();
+ ctx.shadowBlur = 0;
+ ctx.fillStyle = '#ffd6f4';
+ ctx.beginPath(); ctx.arc(fx-1.5, fy-1.5, 2.2*fp, 0, Math.PI*2); ctx.fill();
+ ctx.restore();
+
+ // snake — interpolated, glowing, hue gradient head→tail
+ if(snake){
+ const n = snake.length;
+ const frozen = (state !== 'play' || paused);
+ const tt = frozen ? 1 : Math.min(1, acc/stepMs);
+ ctx.save();
+ ctx.lineCap = 'round';
+ for(let i=n-1;i>=0;i--){
+ const cur = snake[i];
+ const prev = prevSnake[Math.min(i, prevSnake.length-1)] || cur;
+ const x = (lerp(prev.x, cur.x, tt)+.5)*CELL;
+ const y = (lerp(prev.y, cur.y, tt)+.5)*CELL;
+ const k = i/Math.max(1, n-1); // 0 head → 1 tail
+ const hue = 160 + k*60; // green-cyan → blue
+ const r = i===0 ? 8.5 : lerp(7.5, 4, k);
+ ctx.shadowColor = `hsl(${hue} 100% 60%)`;
+ ctx.shadowBlur = i===0 ? 20 : 12;
+ ctx.fillStyle = `hsl(${hue} 100% ${i===0?68:55}%)`;
+ ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI*2); ctx.fill();
+ }
+ // eyes
+ const hcur = snake[0], hprev = prevSnake[0] || hcur;
+ const hx = (lerp(hprev.x, hcur.x, tt)+.5)*CELL;
+ const hy = (lerp(hprev.y, hcur.y, tt)+.5)*CELL;
+ ctx.shadowBlur = 0;
+ ctx.fillStyle = '#04101c';
+ const ex = dir.y !== 0 ? 3.2 : 0, ey = dir.x !== 0 ? 3.2 : 0;
+ const ox = dir.x*2.5, oy = dir.y*2.5;
+ ctx.beginPath(); ctx.arc(hx+ox+ex, hy+oy+ey, 1.7, 0, Math.PI*2); ctx.fill();
+ ctx.beginPath(); ctx.arc(hx+ox-ex, hy+oy-ey, 1.7, 0, Math.PI*2); ctx.fill();
+ ctx.restore();
+ }
+
+ // particles
+ for(let i=particles.length-1;i>=0;i--){
+ const p = particles[i];
+ p.life -= p.decay*t;
+ if(p.life <= 0){ particles.splice(i,1); continue; }
+ p.x += p.vx*t; p.y += p.vy*t;
+ p.vx *= .96; p.vy *= .96;
+ ctx.globalAlpha = Math.max(0, p.life);
+ ctx.shadowColor = p.c; ctx.shadowBlur = 8;
+ ctx.fillStyle = p.c;
+ ctx.beginPath(); ctx.arc(p.x, p.y, p.r*p.life, 0, Math.PI*2); ctx.fill();
+ }
+ ctx.globalAlpha = 1; ctx.shadowBlur = 0;
+
+ // pause veil
+ if(paused && state === 'play'){
+ ctx.fillStyle = 'rgba(4,6,18,.55)';
+ ctx.fillRect(0,0,W,H);
+ ctx.fillStyle = '#e6f1ff';
+ ctx.font = '700 26px system-ui, sans-serif';
+ ctx.textAlign = 'center';
+ ctx.shadowColor = '#00f0ff'; ctx.shadowBlur = 16;
+ ctx.fillText('PAUSED', W/2, H/2);
+ ctx.shadowBlur = 0;
+ }
+ ctx.restore();
+ }
+
+ // ---------- main loop ----------
+ function frame(now){
+ if(lastT === undefined) lastT = now;
+ let dt = Math.min(50, now - lastT);
+ lastT = now;
+ if(state === 'play' && !paused){
+ acc += dt;
+ while(acc >= stepMs && state === 'play'){
+ acc -= stepMs;
+ step();
+ }
+ }
+ draw(dt/1000);
+ requestAnimationFrame(frame);
+ }
+
+ reset();
+ state = 'menu';
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/673cf6b31c78/claude-code.png b/data/artifacts/673cf6b31c78/claude-code.png
new file mode 100644
index 0000000..fcf2435
Binary files /dev/null and b/data/artifacts/673cf6b31c78/claude-code.png differ
diff --git a/data/artifacts/673cf6b31c78/gemma3-12b.html b/data/artifacts/673cf6b31c78/gemma3-12b.html
new file mode 100644
index 0000000..e56d92a
--- /dev/null
+++ b/data/artifacts/673cf6b31c78/gemma3-12b.html
@@ -0,0 +1,188 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Neon Snake</title>
+ <style>
+ body {
+ background-color: #222;
+ overflow: hidden;
+ margin: 0;
+ font-family: monospace;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ }
+
+ canvas {
+ background-color: #333;
+ border: 2px solid #444;
+ box-shadow: 0 0 20px rgba(0, 255, 255, 0.5); /* Neon glow */
+ }
+
+ #score {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ color: #0f0;
+ font-size: 16px;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="gameCanvas" width="400" height="400"></canvas>
+ <div id="score">Score: 0</div>
+
+ <script>
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+ let scoreDisplay = document.getElementById('score');
+
+ const gridSize = 20;
+ let snake = [];
+ let food = {};
+ let direction = 'right';
+ let speed = 150; // Initial speed in milliseconds
+ let gameInterval;
+ let score = 0;
+
+
+ function getRandomGridSize() {
+ return Math.floor(Math.random() * (canvas.width / gridSize));
+ }
+
+ function createFood() {
+ food.x = getRandomGridSize() * gridSize;
+ food.y = getRandomGridSize() * gridSize;
+ if (snake.some(segment => segment.x === food.x && segment.y === food.y)) {
+ createFood(); // Ensure food doesn't spawn on snake
+ }
+ }
+
+ function drawSnake() {
+ ctx.fillStyle = '#0f0';
+ for (let segment of snake) {
+ ctx.fillRect(segment.x, segment.y, gridSize, gridSize);
+ }
+ }
+
+ function drawFood() {
+ ctx.fillStyle = 'red';
+ ctx.fillRect(food.x, food.y, gridSize, gridSize);
+ }
+
+
+ function updateGame() {
+ let headX = snake[0].x;
+ let headY = snake[0].y;
+
+ switch (direction) {
+ case 'up': headY -= gridSize; break;
+ case 'down': headY += gridSize; break;
+ case 'left': headX -= gridSize; break;
+ case 'right': headX += gridSize; break;
+ }
+
+ // Collision detection (walls)
+ if (headX < 0 || headX >= canvas.width || headY < 0 || headY >= canvas.height) {
+ gameOver();
+ return;
+ }
+
+
+ let newHead = { x: headX, y: headY };
+ snake.unshift(newHead); // Add head to the beginning
+
+ // Check for self-collision
+ if (snake.some((segment, index) => segment.x === newHead.x && segment.y === newHead.y && index !== 0)) {
+ gameOver();
+ return;
+ }
+
+
+ if (newHead.x === food.x && newHead.y === food.y) {
+ score++;
+ scoreDisplay.innerText = `Score: ${score}`;
+ createFood();
+ } else {
+ snake.pop(); // Remove tail if no food eaten
+ }
+
+ drawSnake();
+ drawFood();
+
+ }
+
+
+
+ function gameOver() {
+ clearInterval(gameInterval);
+ ctx.fillStyle = 'white';
+ ctx.font = '24px monospace';
+ ctx.textAlign = 'center';
+ ctx.fillText('Game Over', canvas.width / 2, canvas.height / 2);
+
+ let restartButton = document.createElement('button');
+ restartButton.innerText = 'Restart';
+ restartButton.style.position = "absolute";
+ restartButton.style.top = "50%";
+ restartButton.style.left = "50%";
+ restartButton.style.transform = "translate(-50%, -50%)";
+ restartButton.onclick = startGame;
+ document.body.appendChild(restartButton);
+ }
+
+
+ function startGame() {
+ snake = [{ x: 100, y: 100 }]; // Start position
+ food = {};
+ direction = 'right';
+ score = 0;
+ scoreDisplay.innerText = `Score: ${score}`;
+ createFood();
+
+ speed = 150; // Reset speed
+ gameInterval = setInterval(updateGame, speed);
+
+ // Clear any existing restart button
+ let restartButton = document.querySelector('button');
+ if (restartButton) {
+ document.body.removeChild(restartButton);
+ }
+ ctx.clearRect(0, 0, canvas.width, canvas.height); //clear the screen
+
+ }
+
+
+
+ function keydownHandler(event) {
+ let newDirection;
+ switch (event.key) {
+ case 'ArrowUp': newDirection = 'up'; break;
+ case 'ArrowDown': newDirection = 'down'; break;
+ case 'ArrowLeft': newDirection = 'left'; break;
+ case 'ArrowRight': newDirection = 'right'; break;
+ case 'w': newDirection = 'up'; break;
+ case 's': newDirection = 'down'; break;
+ case 'a': newDirection = 'left'; break;
+ case 'd': newDirection = 'right'; break;
+ default: return; // Ignore other keys
+ }
+
+ // Prevent reversing direction immediately
+ if (direction !== newDirection) {
+ direction = newDirection;
+ }
+ }
+
+
+ startGame(); // Start the game initially.
+ document.addEventListener('keydown', keydownHandler);
+
+
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/673cf6b31c78/gemma3-12b.png b/data/artifacts/673cf6b31c78/gemma3-12b.png
new file mode 100644
index 0000000..6c29a49
Binary files /dev/null and b/data/artifacts/673cf6b31c78/gemma3-12b.png differ
diff --git a/data/artifacts/673cf6b31c78/gpt.html b/data/artifacts/673cf6b31c78/gpt.html
new file mode 100644
index 0000000..3fa4ff7
--- /dev/null
+++ b/data/artifacts/673cf6b31c78/gpt.html
@@ -0,0 +1,1247 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<title>Neon Snake</title>
+<style>
+ :root {
+ --bg: #050510;
+ --grid: rgba(255,255,255,0.03);
+ --snake: #00f6ff;
+ --snake-head: #00ff99;
+ --food: #ff2079;
+ --accent: #ffe45e;
+ --danger: #ff4b5c;
+ --text-main: #f5f7fb;
+ --panel-bg: rgba(7, 7, 20, 0.9);
+ }
+
+ * {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ }
+
+ html, body {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ background: radial-gradient(circle at top, #151537 0%, #050510 50%, #000 100%);
+ color: var(--text-main);
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ }
+
+ body {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .frame {
+ position: relative;
+ width: min(90vw, 780px);
+ max-width: 900px;
+ aspect-ratio: 4 / 3;
+ padding: 18px 18px 22px;
+ border-radius: 18px;
+ background:
+ radial-gradient(circle at top left, rgba(0, 255, 255, 0.18), transparent 55%),
+ radial-gradient(circle at bottom right, rgba(255, 0, 153, 0.2), transparent 60%),
+ rgba(5, 5, 20, 0.95);
+ box-shadow:
+ 0 0 0 1px rgba(255, 255, 255, 0.02),
+ 0 32px 80px rgba(0, 0, 0, 0.95),
+ 0 0 40px rgba(0, 255, 255, 0.2);
+ display: flex;
+ flex-direction: column;
+ backdrop-filter: blur(12px);
+ }
+
+ .frame::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ border-radius: inherit;
+ padding: 1px;
+ background:
+ linear-gradient(120deg,
+ rgba(0, 255, 255, 0.7),
+ rgba(255, 0, 153, 0.7),
+ rgba(255, 222, 89, 0.7),
+ rgba(0, 255, 255, 0.7));
+ -webkit-mask:
+ linear-gradient(#000 0 0) content-box,
+ linear-gradient(#000 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ opacity: 0.5;
+ pointer-events: none;
+ }
+
+ header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 10px;
+ z-index: 2;
+ }
+
+ .title {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ font-size: 13px;
+ color: rgba(230, 235, 255, 0.8);
+ }
+
+ .title span {
+ padding: 2px 7px;
+ border-radius: 999px;
+ background: radial-gradient(circle at 30% 0,
+ rgba(0, 255, 255, 0.7),
+ rgba(0, 180, 255, 0.2));
+ color: #031013;
+ font-weight: 700;
+ box-shadow: 0 0 12px rgba(0, 255, 255, 0.7);
+ }
+
+ .title small {
+ opacity: 0.7;
+ font-weight: 500;
+ }
+
+ .info-row {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ font-size: 13px;
+ }
+
+ .chip {
+ padding: 4px 9px;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ color: rgba(240, 244, 255, 0.9);
+ }
+
+ .chip-key {
+ font-size: 10px;
+ padding: 1px 4px;
+ border-radius: 3px;
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ }
+
+ .chip span {
+ opacity: 0.75;
+ }
+
+ .scoreboard {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-family: "SF Mono", ui-monospace, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ font-size: 13px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ }
+
+ .score-label {
+ opacity: 0.65;
+ }
+
+ .score-value {
+ min-width: 52px;
+ text-align: right;
+ color: var(--accent);
+ text-shadow: 0 0 10px rgba(255, 228, 94, 0.7);
+ }
+
+ .score-value#bestScore {
+ color: #9effff;
+ text-shadow: 0 0 10px rgba(128, 255, 255, 0.7);
+ }
+
+ .toggles {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+
+ .toggle {
+ cursor: pointer;
+ user-select: none;
+ }
+
+ .toggle-inner {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 9px;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ font-size: 12px;
+ color: rgba(235, 240, 255, 0.9);
+ }
+
+ .toggle-dot {
+ width: 9px;
+ height: 9px;
+ border-radius: 50%;
+ background: radial-gradient(circle, #444, #111);
+ box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1) inset;
+ }
+
+ .toggle-on .toggle-dot {
+ background: radial-gradient(circle at 30% 0, #0f0, #090);
+ box-shadow:
+ 0 0 0 1px rgba(0, 255, 153, 0.4),
+ 0 0 8px rgba(0, 255, 153, 0.9);
+ }
+
+ .toggle-label {
+ opacity: 0.8;
+ }
+
+ .game-shell {
+ position: relative;
+ flex: 1;
+ margin-top: 10px;
+ border-radius: 16px;
+ overflow: hidden;
+ background:
+ radial-gradient(circle at 0 0, rgba(0, 255, 255, 0.18), transparent 60%),
+ radial-gradient(circle at 100% 100%, rgba(255, 0, 153, 0.18), transparent 60%),
+ radial-gradient(circle at 20% 100%, rgba(0, 255, 153, 0.2), transparent 60%),
+ #050510;
+ box-shadow:
+ 0 0 30px rgba(0, 0, 0, 0.9) inset,
+ 0 0 40px rgba(0, 255, 255, 0.12);
+ display: grid;
+ grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.95fr);
+ gap: 12px;
+ padding: 12px;
+ }
+
+ @media (max-width: 720px) {
+ .game-shell {
+ grid-template-columns: minmax(0, 1fr);
+ grid-template-rows: minmax(0, 1.4fr) minmax(0, 0.9fr);
+ }
+ }
+
+ .playfield-wrap {
+ position: relative;
+ border-radius: 14px;
+ overflow: hidden;
+ background:
+ repeating-linear-gradient(
+ 90deg,
+ rgba(255, 255, 255, 0.03) 0,
+ rgba(255, 255, 255, 0.03) 1px,
+ transparent 1px,
+ transparent 22px
+ ),
+ repeating-linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.03) 0,
+ rgba(255, 255, 255, 0.03) 1px,
+ transparent 1px,
+ transparent 22px
+ ),
+ radial-gradient(circle at top, rgba(0, 255, 255, 0.12), transparent 60%),
+ radial-gradient(circle at bottom, rgba(255, 0, 204, 0.12), transparent 60%),
+ #02030a;
+ box-shadow:
+ 0 0 0 1px rgba(0, 255, 255, 0.25),
+ 0 0 30px rgba(0, 255, 255, 0.35),
+ 0 0 60px rgba(0, 0, 0, 1) inset;
+ isolation: isolate;
+ }
+
+ canvas#game {
+ width: 100%;
+ height: 100%;
+ display: block;
+ image-rendering: crisp-edges;
+ image-rendering: pixelated;
+ }
+
+ .hud-overlay {
+ position: absolute;
+ inset: 8px 10px 10px;
+ pointer-events: none;
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ font-size: 11px;
+ color: rgba(230, 240, 255, 0.7);
+ text-transform: uppercase;
+ letter-spacing: 0.15em;
+ font-family: "SF Mono", ui-monospace, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ mix-blend-mode: screen;
+ }
+
+ .hud-overlay .hint {
+ padding: 3px 7px;
+ border-radius: 999px;
+ background: rgba(0, 0, 0, 0.55);
+ border: 1px solid rgba(0, 255, 255, 0.5);
+ box-shadow: 0 0 12px rgba(0, 255, 255, 0.9);
+ color: rgba(205, 255, 255, 0.9);
+ }
+
+ .hud-overlay .status-badge {
+ padding: 3px 7px;
+ border-radius: 999px;
+ background: rgba(0, 0, 0, 0.55);
+ border: 1px solid rgba(255, 255, 255, 0.25);
+ opacity: 0.8;
+ }
+
+ .side-panel {
+ position: relative;
+ border-radius: 14px;
+ padding: 12px 13px 11px;
+ background:
+ radial-gradient(circle at 0 0, rgba(0, 255, 255, 0.2), transparent 55%),
+ radial-gradient(circle at 100% 0, rgba(255, 0, 170, 0.26), transparent 60%),
+ var(--panel-bg);
+ box-shadow:
+ 0 0 0 1px rgba(255, 255, 255, 0.05),
+ 0 20px 45px rgba(0, 0, 0, 0.9);
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ font-size: 12px;
+ color: rgba(230, 235, 255, 0.9);
+ }
+
+ .side-panel h2 {
+ font-size: 13px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ margin-bottom: 3px;
+ color: rgba(245, 248, 255, 0.9);
+ }
+
+ .side-panel h2 span {
+ color: var(--accent);
+ text-shadow: 0 0 8px rgba(255, 228, 94, 0.8);
+ }
+
+ .pill-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ margin-bottom: 3px;
+ }
+
+ .pill {
+ padding: 3px 7px;
+ border-radius: 999px;
+ background: rgba(0, 0, 0, 0.4);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 11px;
+ color: rgba(235, 240, 255, 0.85);
+ }
+
+ .pill .key {
+ font-size: 9px;
+ padding: 1px 4px;
+ border-radius: 4px;
+ border: 1px solid rgba(255, 255, 255, 0.22);
+ background: radial-gradient(circle at 30% 0, rgba(0, 255, 255, 0.4), rgba(0, 0, 0, 0.8));
+ color: #e7fcff;
+ box-shadow: 0 0 8px rgba(0, 255, 255, 0.7);
+ }
+
+ .pill .desc {
+ opacity: 0.8;
+ }
+
+ .meta {
+ font-size: 11px;
+ line-height: 1.4;
+ opacity: 0.86;
+ }
+
+ .meta strong {
+ color: #ffffff;
+ }
+
+ .metrics {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 6px;
+ margin-top: 5px;
+ }
+
+ .metric {
+ padding: 6px 7px;
+ border-radius: 9px;
+ background: rgba(5, 8, 24, 0.9);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ }
+
+ .metric-label {
+ font-size: 10px;
+ text-transform: uppercase;
+ letter-spacing: 0.14em;
+ opacity: 0.7;
+ margin-bottom: 2px;
+ }
+
+ .metric-value {
+ font-size: 13px;
+ font-family: "SF Mono", ui-monospace, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ }
+
+ .metric-value span {
+ color: var(--accent);
+ text-shadow: 0 0 7px rgba(255, 228, 94, 0.7);
+ }
+
+ .metric-value small {
+ font-size: 11px;
+ opacity: 0.7;
+ margin-left: 4px;
+ }
+
+ .footer-hint {
+ margin-top: auto;
+ padding-top: 4px;
+ font-size: 10px;
+ opacity: 0.7;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .footer-hint span {
+ white-space: nowrap;
+ }
+
+ .footer-hint strong {
+ color: #ffffff;
+ }
+
+ .btn {
+ -webkit-appearance: none;
+ appearance: none;
+ outline: none;
+ border: none;
+ cursor: pointer;
+ padding: 5px 11px;
+ border-radius: 999px;
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.14em;
+ font-weight: 600;
+ background: radial-gradient(circle at 30% 0, #00f6ff, #006eff);
+ color: #001117;
+ box-shadow:
+ 0 0 0 1px rgba(255, 255, 255, 0.18),
+ 0 0 16px rgba(0, 232, 255, 0.9);
+ transition: transform 0.08s ease-out, box-shadow 0.08s ease-out, opacity 0.12s;
+ }
+
+ .btn:active {
+ transform: translateY(1px) scale(0.98);
+ box-shadow:
+ 0 0 0 1px rgba(255, 255, 255, 0.18),
+ 0 0 7px rgba(0, 232, 255, 0.7);
+ }
+
+ .overlay {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ pointer-events: none;
+ }
+
+ .overlay-inner {
+ min-width: 60%;
+ max-width: 88%;
+ padding: 18px 20px 16px;
+ border-radius: 14px;
+ background:
+ radial-gradient(circle at 0 0, rgba(0, 255, 255, 0.25), transparent 55%),
+ radial-gradient(circle at 100% 100%, rgba(255, 0, 204, 0.26), transparent 60%),
+ rgba(3, 5, 18, 0.98);
+ box-shadow:
+ 0 0 0 1px rgba(255, 255, 255, 0.18),
+ 0 22px 60px rgba(0, 0, 0, 0.95),
+ 0 0 45px rgba(0, 255, 255, 0.65);
+ transform: translateY(10px);
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.16s ease-out, transform 0.16s ease-out;
+ }
+
+ .overlay.show .overlay-inner {
+ opacity: 1;
+ transform: translateY(0);
+ pointer-events: auto;
+ }
+
+ .overlay-header {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ margin-bottom: 7px;
+ }
+
+ .overlay-title {
+ font-size: 18px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: #ffffff;
+ }
+
+ .overlay-sub {
+ font-size: 11px;
+ opacity: 0.8;
+ text-transform: uppercase;
+ letter-spacing: 0.14em;
+ }
+
+ .overlay-score {
+ margin: 4px 0 6px;
+ font-family: "SF Mono", ui-monospace, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ display: flex;
+ flex-wrap: wrap;
+ row-gap: 3px;
+ column-gap: 18px;
+ }
+
+ .overlay-score dl {
+ display: flex;
+ gap: 6px;
+ align-items: baseline;
+ }
+
+ .overlay-score dt {
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.12em;
+ opacity: 0.7;
+ }
+
+ .overlay-score dd {
+ font-size: 15px;
+ }
+
+ .overlay-score-main {
+ color: var(--accent);
+ text-shadow: 0 0 14px rgba(255, 228, 94, 1);
+ }
+
+ .overlay-score-best {
+ color: #9effff;
+ text-shadow: 0 0 14px rgba(120, 255, 255, 1);
+ }
+
+ .overlay-body {
+ font-size: 12px;
+ opacity: 0.9;
+ margin-bottom: 10px;
+ }
+
+ .overlay-body strong {
+ color: #ffffff;
+ }
+
+ .overlay-actions {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .overlay-actions .pill {
+ background: rgba(0, 0, 0, 0.6);
+ border-color: rgba(255, 255, 255, 0.14);
+ font-size: 11px;
+ }
+
+ .overlay-actions .pill .key {
+ background: transparent;
+ box-shadow: none;
+ border-color: rgba(255, 255, 255, 0.4);
+ }
+
+ .overlay-actions .btn-secondary {
+ background: transparent;
+ color: rgba(235, 240, 255, 0.9);
+ border-radius: 999px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ padding: 4px 10px;
+ box-shadow: none;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ font-size: 10px;
+ }
+
+ .overlay-actions .btn-secondary:active {
+ transform: translateY(1px);
+ }
+
+ .overlay .danger {
+ color: var(--danger);
+ text-shadow: 0 0 10px rgba(255, 75, 92, 0.9);
+ }
+
+ @keyframes pulse-glow {
+ 0% { opacity: 0.5; box-shadow: 0 0 6px rgba(0, 255, 255, 0.7); }
+ 50% { opacity: 1; box-shadow: 0 0 14px rgba(0, 255, 255, 1); }
+ 100% { opacity: 0.5; box-shadow: 0 0 6px rgba(0, 255, 255, 0.7); }
+ }
+
+ .blink {
+ animation: pulse-glow 1.2s ease-in-out infinite;
+ }
+</style>
+</head>
+<body>
+<div class="frame">
+ <header>
+ <div class="title">
+ <span>SNAKE</span>
+ <small>NEON EDITION</small>
+ </div>
+ <div class="info-row">
+ <div class="scoreboard">
+ <span class="score-label">Score</span>
+ <span id="score" class="score-value">000</span>
+ <span class="score-label">Best</span>
+ <span id="bestScore" class="score-value">000</span>
+ </div>
+ <div class="chip">
+ <div class="chip-key">WASD</div>
+ <span>or</span>
+ <div class="chip-key">←↑→↓</div>
+ </div>
+ <div class="toggles">
+ <div id="soundToggle" class="toggle">
+ <div class="toggle-inner toggle-on">
+ <div class="toggle-dot"></div>
+ <span class="toggle-label">Sound</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </header>
+
+ <div class="game-shell">
+ <div class="playfield-wrap">
+ <canvas id="game"></canvas>
+ <div class="hud-overlay">
+ <div class="hint blink">Press SPACE to start</div>
+ <div class="status-badge" id="speedBadge">SPEED x1.0</div>
+ </div>
+ <div class="overlay" id="overlay">
+ <div class="overlay-inner">
+ <div class="overlay-header">
+ <div class="overlay-title danger" id="overlayTitle">GAME OVER</div>
+ <div class="overlay-sub">Neon Arena</div>
+ </div>
+ <div class="overlay-score">
+ <dl>
+ <dt>Score</dt>
+ <dd class="overlay-score-main" id="overlayScore">000</dd>
+ </dl>
+ <dl>
+ <dt>Best</dt>
+ <dd class="overlay-score-best" id="overlayBest">000</dd>
+ </dl>
+ </div>
+ <div class="overlay-body" id="overlayMsg">
+ You crashed into your own trail. <strong>Tap Space</strong> or click
+ <strong>Restart</strong> to charge up the grid and try again.
+ </div>
+ <div class="overlay-actions">
+ <button class="btn" id="btnRestart">Restart</button>
+ <div class="pill">
+ <span class="key">SPACE</span>
+ <span class="desc">quick restart</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <aside class="side-panel">
+ <h2><span>Controls</span> & Tips</h2>
+ <div class="pill-row">
+ <div class="pill">
+ <span class="key">W</span>
+ <span class="key">A</span>
+ <span class="key">S</span>
+ <span class="key">D</span>
+ <span class="desc">Move</span>
+ </div>
+ <div class="pill">
+ <span class="key">↑</span>
+ <span class="key">↓</span>
+ <span class="key">←</span>
+ <span class="key">→</span>
+ <span class="desc">Also move</span>
+ </div>
+ <div class="pill">
+ <span class="key">SPACE</span>
+ <span class="desc">Start / Pause</span>
+ </div>
+ <div class="pill">
+ <span class="key">R</span>
+ <span class="desc">Instant restart</span>
+ </div>
+ </div>
+ <div class="meta">
+ Guide the neon serpent across the grid, collecting pulses of <strong>energy</strong>.
+ Each bite grows your tail and <strong>amplifies</strong> your speed.
+ Avoid colliding with walls or yourself—or the grid will overload.
+ </div>
+ <div class="metrics">
+ <div class="metric">
+ <div class="metric-label">Length</div>
+ <div class="metric-value"><span id="lengthMetric">3</span> <small>segments</small></div>
+ </div>
+ <div class="metric">
+ <div class="metric-label">Speed</div>
+ <div class="metric-value"><span id="speedMetric">1.0x</span> <small>multiplier</small></div>
+ </div>
+ <div class="metric">
+ <div class="metric-label">Session</div>
+ <div class="metric-value"><span id="sessionMetric">0</span> <small>games</small></div>
+ </div>
+ <div class="metric">
+ <div class="metric-label">Best Streak</div>
+ <div class="metric-value"><span id="streakMetric">0</span> <small>food chain</small></div>
+ </div>
+ </div>
+ <div class="footer-hint">
+ <span>Walls are <strong>solid</strong>: plan ahead.</span>
+ <button class="btn-secondary" id="btnPauseToggle">Pause</button>
+ </div>
+ </aside>
+ </div>
+</div>
+
+<script>
+(function () {
+ const canvas = document.getElementById('game');
+ const ctx = canvas.getContext('2d');
+
+ const scoreEl = document.getElementById('score');
+ const bestScoreEl = document.getElementById('bestScore');
+ const overlay = document.getElementById('overlay');
+ const overlayTitle = document.getElementById('overlayTitle');
+ const overlayScore = document.getElementById('overlayScore');
+ const overlayBest = document.getElementById('overlayBest');
+ const overlayMsg = document.getElementById('overlayMsg');
+ const btnRestart = document.getElementById('btnRestart');
+ const soundToggle = document.getElementById('soundToggle');
+ const speedBadge = document.getElementById('speedBadge');
+ const lengthMetric = document.getElementById('lengthMetric');
+ const speedMetric = document.getElementById('speedMetric');
+ const sessionMetric = document.getElementById('sessionMetric');
+ const streakMetric = document.getElementById('streakMetric');
+ const btnPauseToggle = document.getElementById('btnPauseToggle');
+
+ let gridCols = 24;
+ let gridRows = 18;
+ const baseSpeed = 7; // cells per second
+ const maxSpeed = 18;
+ const speedIncreasePerFood = 0.35;
+ const tailGrowthPerFood = 1;
+
+ let cellSize;
+ let lastTime = 0;
+ let accumulator = 0;
+ let stepInterval = 1 / baseSpeed;
+
+ let snake;
+ let dir;
+ let nextDir;
+ let food;
+ let score = 0;
+ let bestScore = 0;
+ let running = false;
+ let paused = false;
+ let gameOver = false;
+ let soundOn = true;
+ let sessionGames = 0;
+ let longestStreak = 0;
+ let currentStreak = 0;
+
+ function initCanvas() {
+ const rect = canvas.parentElement.getBoundingClientRect();
+ const padding = 24;
+ const w = rect.width - padding;
+ const h = rect.height - padding;
+ const cellSizeX = Math.floor(w / gridCols);
+ const cellSizeY = Math.floor(h / gridRows);
+ cellSize = Math.max(12, Math.min(cellSizeX, cellSizeY));
+ canvas.width = cellSize * gridCols;
+ canvas.height = cellSize * gridRows;
+ }
+
+ function resetGame() {
+ const startX = Math.floor(gridCols / 2);
+ const startY = Math.floor(gridRows / 2);
+ snake = [
+ { x: startX - 1, y: startY },
+ { x: startX, y: startY },
+ { x: startX + 1, y: startY }
+ ];
+ dir = { x: 1, y: 0 };
+ nextDir = { x: 1, y: 0 };
+ placeFood();
+ score = 0;
+ currentStreak = 0;
+ updateScoreUI();
+ updateMetrics();
+ stepInterval = 1 / baseSpeed;
+ updateSpeedUI();
+ running = false;
+ paused = false;
+ gameOver = false;
+ accumulator = 0;
+ overlay.classList.remove('show');
+ }
+
+ function updateScoreUI() {
+ scoreEl.textContent = score.toString().padStart(3, '0');
+ bestScoreEl.textContent = bestScore.toString().padStart(3, '0');
+ }
+
+ function updateMetrics() {
+ lengthMetric.textContent = snake.length;
+ sessionMetric.textContent = sessionGames;
+ streakMetric.textContent = longestStreak;
+ }
+
+ function updateSpeedUI() {
+ const speedMultiplier = (1 / stepInterval) / baseSpeed;
+ const label = 'SPEED x' + speedMultiplier.toFixed(1);
+ speedBadge.textContent = label;
+ speedMetric.textContent = speedMultiplier.toFixed(1) + 'x';
+ }
+
+ function randomInt(max) {
+ return Math.floor(Math.random() * max);
+ }
+
+ function placeFood() {
+ let valid = false;
+ let x, y;
+ while (!valid) {
+ x = randomInt(gridCols);
+ y = randomInt(gridRows);
+ valid = !snake.some(s => s.x === x && s.y === y);
+ }
+ food = { x, y };
+ }
+
+ function playBeep(freq, duration, type, volume) {
+ if (!soundOn) return;
+ try {
+ const AudioCtx = window.AudioContext || window.webkitAudioContext;
+ const ctxAudio = new AudioCtx();
+ const osc = ctxAudio.createOscillator();
+ const gain = ctxAudio.createGain();
+ osc.type = type || 'square';
+ osc.frequency.value = freq;
+ gain.gain.value = volume || 0.08;
+ osc.connect(gain);
+ gain.connect(ctxAudio.destination);
+ osc.start();
+ osc.stop(ctxAudio.currentTime + duration);
+ osc.onended = () => ctxAudio.close();
+ } catch (e) {}
+ }
+
+ function setDirection(dx, dy) {
+ if (gameOver) return;
+ if (!running) running = true;
+ const isOpposite = (dx === -dir.x && dy === -dir.y) || (dx === dir.x && dy === dir.y);
+ if (isOpposite) return;
+ nextDir = { x: dx, y: dy };
+ }
+
+ function isCollision(x, y) {
+ if (x < 0 || x >= gridCols || y < 0 || y >= gridRows) return true;
+ for (let i = 0; i < snake.length; i++) {
+ if (snake[i].x === x && snake[i].y === y) return true;
+ }
+ return false;
+ }
+
+ function step() {
+ if (!running || paused || gameOver) return;
+
+ dir = { ...nextDir };
+ const head = snake[snake.length - 1];
+ const newHead = { x: head.x + dir.x, y: head.y + dir.y };
+
+ if (newHead.x < 0 || newHead.x >= gridCols || newHead.y < 0 || newHead.y >= gridRows) {
+ handleGameOver('You slammed into the wall. The grid doesn\'t forgive sharp turns.');
+ playBeep(90, 0.25, 'sawtooth', 0.16);
+ return;
+ }
+
+ for (let i = 0; i < snake.length; i++) {
+ if (snake[i].x === newHead.x && snake[i].y === newHead.y) {
+ handleGameOver('You crossed your own neon trail. Try a cleaner line.');
+ playBeep(120, 0.3, 'triangle', 0.14);
+ return;
+ }
+ }
+
+ snake.push(newHead);
+
+ if (newHead.x === food.x && newHead.y === food.y) {
+ score += 10;
+ currentStreak++;
+ if (currentStreak > longestStreak) longestStreak = currentStreak;
+ updateScoreUI();
+ updateMetrics();
+ const newSpeed = Math.min(maxSpeed, (1 / stepInterval) + speedIncreasePerFood);
+ stepInterval = 1 / newSpeed;
+ updateSpeedUI();
+ playBeep(620, 0.11, 'square', 0.11);
+ placeFood();
+ } else {
+ for (let i = 0; i < tailGrowthPerFood; i++) {
+ snake.shift();
+ }
+ }
+ lengthMetric.textContent = snake.length;
+ }
+
+ function handleGameOver(message) {
+ gameOver = true;
+ running = false;
+ paused = false;
+ sessionGames++;
+ if (score > bestScore) bestScore = score;
+ updateScoreUI();
+ updateMetrics();
+ overlayTitle.textContent = 'GAME OVER';
+ overlayTitle.classList.add('danger');
+ overlayMsg.textContent = message;
+ overlayScore.textContent = score.toString().padStart(3, '0');
+ overlayBest.textContent = bestScore.toString().padStart(3, '0');
+ overlay.classList.add('show');
+ }
+
+ function drawGrid() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ const gradientBg = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
+ gradientBg.addColorStop(0, '#050614');
+ gradientBg.addColorStop(1, '#02010a');
+ ctx.fillStyle = gradientBg;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ ctx.save();
+ ctx.strokeStyle = 'rgba(0, 255, 255, 0.05)';
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ for (let x = 0; x <= gridCols; x++) {
+ ctx.moveTo(x * cellSize + 0.5, 0);
+ ctx.lineTo(x * cellSize + 0.5, canvas.height);
+ }
+ for (let y = 0; y <= gridRows; y++) {
+ ctx.moveTo(0, y * cellSize + 0.5);
+ ctx.lineTo(canvas.width, y * cellSize + 0.5);
+ }
+ ctx.stroke();
+ ctx.restore();
+
+ const glowGrad = ctx.createRadialGradient(
+ canvas.width / 2,
+ canvas.height / 2,
+ 10,
+ canvas.width / 2,
+ canvas.height / 2,
+ Math.max(canvas.width, canvas.height)
+ );
+ glowGrad.addColorStop(0, 'rgba(0, 255, 255, 0.03)');
+ glowGrad.addColorStop(0.6, 'rgba(0, 0, 0, 0.0)');
+ glowGrad.addColorStop(1, 'rgba(0, 0, 0, 0.7)');
+ ctx.fillStyle = glowGrad;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ }
+
+ function drawFood() {
+ const x = food.x * cellSize;
+ const y = food.y * cellSize;
+ const r = cellSize * 0.36;
+ const cx = x + cellSize / 2;
+ const cy = y + cellSize / 2;
+
+ const glow = ctx.createRadialGradient(cx, cy, r * 0.1, cx, cy, r * 2.2);
+ glow.addColorStop(0, 'rgba(255, 32, 121, 0.9)');
+ glow.addColorStop(0.3, 'rgba(255, 32, 121, 0.45)');
+ glow.addColorStop(1, 'rgba(255, 32, 121, 0)');
+ ctx.save();
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.fillStyle = glow;
+ ctx.fillRect(x - r * 1.2, y - r * 1.2, r * 3.4, r * 3.4);
+ ctx.restore();
+
+ const core = ctx.createRadialGradient(cx, cy, r * 0.2, cx, cy, r);
+ core.addColorStop(0, '#ffe6ff');
+ core.addColorStop(0.4, '#ff6fb3');
+ core.addColorStop(1, '#ff2079');
+ ctx.beginPath();
+ ctx.fillStyle = core;
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.35)';
+ ctx.lineWidth = 1.2;
+ ctx.arc(cx, cy, r * 0.55, -Math.PI / 5, Math.PI / 2.4);
+ ctx.stroke();
+ }
+
+ function drawSnake(time) {
+ const t = (time / 1000) || 0;
+ for (let i = 0; i < snake.length; i++) {
+ const seg = snake[i];
+ const x = seg.x * cellSize;
+ const y = seg.y * cellSize;
+ const isHead = i === snake.length - 1;
+
+ const baseHue = isHead ? 155 : 180;
+ const hueOffset = (i / snake.length) * 60;
+ const pulse = (Math.sin(t * 4 + i * 0.6) + 1) / 2;
+ const lum = 40 + pulse * 20;
+ const color = `hsl(${baseHue + hueOffset}, 90%, ${lum}%)`;
+
+ ctx.save();
+ ctx.beginPath();
+ const inset = cellSize * 0.12;
+ const radius = Math.max(3, cellSize * 0.3);
+ roundedRect(ctx, x + inset, y + inset, cellSize - inset * 2, cellSize - inset * 2, radius);
+ ctx.fillStyle = color;
+ ctx.shadowColor = color;
+ ctx.shadowBlur = isHead ? 24 : 14;
+ ctx.fill();
+ ctx.restore();
+
+ if (isHead) {
+ ctx.save();
+ const cx = x + cellSize / 2;
+ const cy = y + cellSize / 2;
+ const radiusHead = cellSize * 0.38;
+ const grad = ctx.createRadialGradient(cx, cy, radiusHead * 0.1, cx, cy, radiusHead);
+ grad.addColorStop(0, '#e8fff8');
+ grad.addColorStop(0.4, '#00ffcc');
+ grad.addColorStop(1, '#00b88a');
+ ctx.beginPath();
+ ctx.arc(cx, cy, radiusHead, 0, Math.PI * 2);
+ ctx.strokeStyle = grad;
+ ctx.lineWidth = 1.3;
+ ctx.globalAlpha = 0.95;
+ ctx.stroke();
+
+ const eyeOffset = cellSize * 0.16;
+ let ex1 = cx - eyeOffset;
+ let ex2 = cx + eyeOffset;
+ let ey1 = cy - eyeOffset;
+ let ey2 = cy - eyeOffset;
+ if (dir.x === 1) {
+ ex1 = cx + eyeOffset * 0.2;
+ ex2 = cx + eyeOffset * 0.8;
+ ey1 = cy - eyeOffset * 0.4;
+ ey2 = cy + eyeOffset * 0.4;
+ } else if (dir.x === -1) {
+ ex1 = cx - eyeOffset * 0.8;
+ ex2 = cx - eyeOffset * 0.2;
+ ey1 = cy - eyeOffset * 0.4;
+ ey2 = cy + eyeOffset * 0.4;
+ } else if (dir.y === 1) {
+ ex1 = cx - eyeOffset * 0.4;
+ ex2 = cx + eyeOffset * 0.4;
+ ey1 = cy + eyeOffset * 0.6;
+ ey2 = cy + eyeOffset * 0.6;
+ } else if (dir.y === -1) {
+ ex1 = cx - eyeOffset * 0.4;
+ ex2 = cx + eyeOffset * 0.4;
+ ey1 = cy - eyeOffset * 0.8;
+ ey2 = cy - eyeOffset * 0.8;
+ }
+
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.9)';
+ ctx.beginPath();
+ ctx.arc(ex1, ey1, cellSize * 0.08, 0, Math.PI * 2);
+ ctx.arc(ex2, ey2, cellSize * 0.08, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.fillStyle = 'rgba(255,255,255,0.9)';
+ ctx.beginPath();
+ ctx.arc(ex1 + cellSize * 0.02, ey1 - cellSize * 0.01, cellSize * 0.02, 0, Math.PI * 2);
+ ctx.arc(ex2 + cellSize * 0.02, ey2 - cellSize * 0.01, cellSize * 0.02, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.restore();
+ }
+ }
+ }
+
+ function roundedRect(ctx, x, y, width, height, radius) {
+ const r = Math.min(radius, width / 2, height / 2);
+ ctx.moveTo(x + r, y);
+ ctx.arcTo(x + width, y, x + width, y + height, r);
+ ctx.arcTo(x + width, y + height, x, y + height, r);
+ ctx.arcTo(x, y + height, x, y, r);
+ ctx.arcTo(x, y, x + width, y, r);
+ ctx.closePath();
+ }
+
+ function draw(time) {
+ drawGrid();
+ if (food) drawFood();
+ if (snake) drawSnake(time);
+ }
+
+ function loop(timestamp) {
+ if (!lastTime) lastTime = timestamp;
+ const delta = (timestamp - lastTime) / 1000;
+ lastTime = timestamp;
+ accumulator += delta;
+
+ const currentStepInterval = stepInterval;
+ while (accumulator >= currentStepInterval) {
+ accumulator -= currentStepInterval;
+ step();
+ }
+ draw(timestamp);
+ requestAnimationFrame(loop);
+ }
+
+ function togglePause() {
+ if (gameOver) return;
+ if (!running) {
+ running = true;
+ paused = false;
+ btnPauseToggle.textContent = 'Pause';
+ return;
+ }
+ paused = !paused;
+ btnPauseToggle.textContent = paused ? 'Resume' : 'Pause';
+ }
+
+ function instantRestart() {
+ overlay.classList.remove('show');
+ resetGame();
+ running = true;
+ }
+
+ function handleKeyDown(e) {
+ const key = e.key.toLowerCase();
+ if (['arrowup', 'arrowdown', 'arrowleft', 'arrowright', 'w', 'a', 's', 'd'].includes(key)) {
+ e.preventDefault();
+ }
+
+ if (key === ' ' || key === 'spacebar') {
+ e.preventDefault();
+ if (gameOver) {
+ instantRestart();
+ return;
+ }
+ togglePause();
+ return;
+ }
+
+ if (key === 'r') {
+ instantRestart();
+ return;
+ }
+
+ switch (key) {
+ case 'arrowup':
+ case 'w':
+ setDirection(0, -1);
+ break;
+ case 'arrowdown':
+ case 's':
+ setDirection(0, 1);
+ break;
+ case 'arrowleft':
+ case 'a':
+ setDirection(-1, 0);
+ break;
+ case 'arrowright':
+ case 'd':
+ setDirection(1, 0);
+ break;
+ }
+ }
+
+ function setupUI() {
+ soundToggle.addEventListener('click', () => {
+ soundOn = !soundOn;
+ const inner = soundToggle.querySelector('.toggle-inner');
+ if (soundOn) {
+ inner.classList.add('toggle-on');
+ playBeep(420, 0.07, 'sine', 0.08);
+ } else {
+ inner.classList.remove('toggle-on');
+ }
+ });
+
+ btnRestart.addEventListener('click', () => {
+ instantRestart();
+ });
+
+ btnPauseToggle.addEventListener('click', () => {
+ togglePause();
+ });
+ }
+
+ window.addEventListener('resize', () => {
+ initCanvas();
+ draw(performance.now());
+ });
+
+ window.addEventListener('keydown', handleKeyDown);
+
+ (function init() {
+ try {
+ const stored = localStorage.getItem('neonSnakeBest');
+ if (stored) bestScore = parseInt(stored, 10) || 0;
+ } catch (e) {}
+ initCanvas();
+ resetGame();
+ setupUI();
+ requestAnimationFrame(loop);
+ })();
+
+ const originalUpdateScoreUI = updateScoreUI;
+ function overrideScoreUI() {
+ originalUpdateScoreUI();
+ try {
+ localStorage.setItem('neonSnakeBest', bestScore);
+ } catch (e) {}
+ }
+ updateScoreUI = overrideScoreUI;
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/673cf6b31c78/gpt.png b/data/artifacts/673cf6b31c78/gpt.png
new file mode 100644
index 0000000..0a78f9c
Binary files /dev/null and b/data/artifacts/673cf6b31c78/gpt.png differ
diff --git a/data/artifacts/673cf6b31c78/grok.html b/data/artifacts/673cf6b31c78/grok.html
new file mode 100644
index 0000000..473c120
--- /dev/null
+++ b/data/artifacts/673cf6b31c78/grok.html
@@ -0,0 +1,665 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Neon Snake</title>
+<style>
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap');
+
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+
+ body {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: #0a0a12;
+ font-family: 'Orbitron', 'Segoe UI', system-ui, sans-serif;
+ overflow: hidden;
+ color: #e0e0ff;
+ }
+
+ body::before {
+ content: '';
+ position: fixed;
+ inset: 0;
+ background:
+ radial-gradient(ellipse at 20% 30%, rgba(0, 255, 200, 0.06) 0%, transparent 50%),
+ radial-gradient(ellipse at 80% 70%, rgba(255, 0, 128, 0.06) 0%, transparent 50%),
+ radial-gradient(ellipse at 50% 50%, rgba(100, 50, 255, 0.04) 0%, transparent 70%);
+ pointer-events: none;
+ z-index: 0;
+ }
+
+ .wrap {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 16px;
+ }
+
+ h1 {
+ font-size: 1.8rem;
+ font-weight: 900;
+ letter-spacing: 0.35em;
+ text-transform: uppercase;
+ background: linear-gradient(90deg, #00ffc8, #00aaff, #ff00aa);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-shadow: none;
+ filter: drop-shadow(0 0 20px rgba(0, 255, 200, 0.4));
+ }
+
+ .hud {
+ display: flex;
+ gap: 32px;
+ align-items: center;
+ font-size: 0.85rem;
+ letter-spacing: 0.15em;
+ }
+
+ .hud span {
+ color: #00ffc8;
+ text-shadow: 0 0 10px rgba(0, 255, 200, 0.6);
+ }
+
+ .hud .label { color: #6666aa; margin-right: 6px; }
+
+ .canvas-box {
+ position: relative;
+ border-radius: 12px;
+ padding: 3px;
+ background: linear-gradient(135deg, #00ffc8, #00aaff, #ff00aa, #00ffc8);
+ background-size: 300% 300%;
+ animation: borderGlow 6s ease infinite;
+ box-shadow:
+ 0 0 30px rgba(0, 255, 200, 0.25),
+ 0 0 60px rgba(0, 170, 255, 0.15),
+ inset 0 0 20px rgba(0,0,0,0.5);
+ }
+
+ @keyframes borderGlow {
+ 0%, 100% { background-position: 0% 50%; }
+ 50% { background-position: 100% 50%; }
+ }
+
+ canvas {
+ display: block;
+ border-radius: 10px;
+ background: #0d0d1a;
+ }
+
+ .overlay {
+ position: absolute;
+ inset: 3px;
+ border-radius: 10px;
+ background: rgba(8, 8, 20, 0.88);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 16px;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.3s ease;
+ backdrop-filter: blur(4px);
+ }
+
+ .overlay.show {
+ opacity: 1;
+ pointer-events: auto;
+ }
+
+ .overlay h2 {
+ font-size: 2rem;
+ font-weight: 700;
+ letter-spacing: 0.2em;
+ background: linear-gradient(90deg, #ff00aa, #00ffc8);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ }
+
+ .overlay p {
+ color: #8888bb;
+ font-size: 0.8rem;
+ letter-spacing: 0.1em;
+ }
+
+ .overlay .final-score {
+ font-size: 1.1rem;
+ color: #00ffc8;
+ text-shadow: 0 0 12px rgba(0, 255, 200, 0.5);
+ }
+
+ button {
+ margin-top: 8px;
+ padding: 12px 36px;
+ font-family: inherit;
+ font-size: 0.85rem;
+ font-weight: 700;
+ letter-spacing: 0.25em;
+ text-transform: uppercase;
+ color: #0a0a12;
+ background: linear-gradient(135deg, #00ffc8, #00aaff);
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: transform 0.15s, box-shadow 0.15s;
+ box-shadow: 0 0 20px rgba(0, 255, 200, 0.4);
+ }
+
+ button:hover {
+ transform: scale(1.05);
+ box-shadow: 0 0 30px rgba(0, 255, 200, 0.6);
+ }
+
+ button:active { transform: scale(0.97); }
+
+ .hint {
+ font-size: 0.7rem;
+ color: #444466;
+ letter-spacing: 0.12em;
+ }
+
+ .sound-toggle {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ z-index: 2;
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ border: 1px solid rgba(0, 255, 200, 0.3);
+ background: rgba(10, 10, 20, 0.8);
+ color: #00ffc8;
+ font-size: 16px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s;
+ padding: 0;
+ box-shadow: none;
+ }
+
+ .sound-toggle:hover {
+ background: rgba(0, 255, 200, 0.15);
+ transform: none;
+ box-shadow: 0 0 12px rgba(0, 255, 200, 0.3);
+ }
+
+ .sound-toggle.off { color: #555577; border-color: rgba(85, 85, 119, 0.4); }
+</style>
+</head>
+<body>
+<div class="wrap">
+ <h1>NEON SNAKE</h1>
+ <div class="hud">
+ <div><span class="label">SCORE</span><span id="score">0</span></div>
+ <div><span class="label">BEST</span><span id="best">0</span></div>
+ <div><span class="label">SPEED</span><span id="speed">1</span></div>
+ </div>
+ <div class="canvas-box">
+ <button class="sound-toggle" id="soundBtn" title="Toggle sound">🔊</button>
+ <canvas id="c" width="400" height="400"></canvas>
+ <div class="overlay show" id="overlay">
+ <h2 id="overlayTitle">NEON SNAKE</h2>
+ <p id="overlayMsg">Arrow keys or WASD to move</p>
+ <div class="final-score" id="finalScore" style="display:none"></div>
+ <button id="startBtn">START</button>
+ </div>
+ </div>
+ <p class="hint">ARROWS / WASD · P TO PAUSE · EAT TO GROW</p>
+</div>
+<script>
+(function () {
+ const canvas = document.getElementById('c');
+ const ctx = canvas.getContext('2d');
+ const W = canvas.width;
+ const H = canvas.height;
+ const GRID = 20;
+ const COLS = W / GRID;
+ const ROWS = H / GRID;
+
+ const scoreEl = document.getElementById('score');
+ const bestEl = document.getElementById('best');
+ const speedEl = document.getElementById('speed');
+ const overlay = document.getElementById('overlay');
+ const overlayTitle = document.getElementById('overlayTitle');
+ const overlayMsg = document.getElementById('overlayMsg');
+ const finalScore = document.getElementById('finalScore');
+ const startBtn = document.getElementById('startBtn');
+ const soundBtn = document.getElementById('soundBtn');
+
+ let best = 0;
+ try { best = parseInt(localStorage.getItem('neonSnakeBest') || '0', 10); } catch (_) {}
+ bestEl.textContent = best;
+
+ let snake, dir, nextDir, food, score, speedLevel, tickMs, acc, lastTime;
+ let running = false, paused = false, gameOver = false;
+ let soundOn = true;
+ let particles = [];
+ let foodPulse = 0;
+ let audioCtx = null;
+
+ function ensureAudio() {
+ if (!audioCtx) {
+ try { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } catch (_) {}
+ }
+ if (audioCtx && audioCtx.state === 'suspended') audioCtx.resume();
+ }
+
+ function beep(freq, dur, type, vol) {
+ if (!soundOn || !audioCtx) return;
+ try {
+ const o = audioCtx.createOscillator();
+ const g = audioCtx.createGain();
+ o.type = type || 'square';
+ o.frequency.value = freq;
+ g.gain.value = vol || 0.06;
+ g.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + dur);
+ o.connect(g);
+ g.connect(audioCtx.destination);
+ o.start();
+ o.stop(audioCtx.currentTime + dur);
+ } catch (_) {}
+ }
+
+ function playEat() { beep(520, 0.08, 'square', 0.07); setTimeout(() => beep(780, 0.1, 'square', 0.05), 60); }
+ function playDie() { beep(180, 0.15, 'sawtooth', 0.08); setTimeout(() => beep(100, 0.3, 'sawtooth', 0.06), 100); }
+ function playMove() { beep(120, 0.03, 'triangle', 0.02); }
+ function playStart() { beep(330, 0.08, 'square', 0.05); setTimeout(() => beep(440, 0.08, 'square', 0.05), 80); setTimeout(() => beep(660, 0.12, 'square', 0.06), 160); }
+
+ function randCell() {
+ return { x: Math.floor(Math.random() * COLS), y: Math.floor(Math.random() * ROWS) };
+ }
+
+ function spawnFood() {
+ let f;
+ do {
+ f = randCell();
+ } while (snake.some(s => s.x === f.x && s.y === f.y));
+ food = f;
+ }
+
+ function reset() {
+ const cx = Math.floor(COLS / 2);
+ const cy = Math.floor(ROWS / 2);
+ snake = [
+ { x: cx, y: cy },
+ { x: cx - 1, y: cy },
+ { x: cx - 2, y: cy }
+ ];
+ dir = { x: 1, y: 0 };
+ nextDir = { x: 1, y: 0 };
+ score = 0;
+ speedLevel = 1;
+ tickMs = 140;
+ acc = 0;
+ particles = [];
+ gameOver = false;
+ paused = false;
+ spawnFood();
+ scoreEl.textContent = '0';
+ speedEl.textContent = '1';
+ }
+
+ function showOverlay(title, msg, showScore) {
+ overlayTitle.textContent = title;
+ overlayMsg.textContent = msg;
+ if (showScore) {
+ finalScore.style.display = 'block';
+ finalScore.textContent = 'SCORE ' + score;
+ } else {
+ finalScore.style.display = 'none';
+ }
+ startBtn.textContent = gameOver ? 'RESTART' : 'START';
+ overlay.classList.add('show');
+ }
+
+ function hideOverlay() {
+ overlay.classList.remove('show');
+ }
+
+ function startGame() {
+ ensureAudio();
+ reset();
+ running = true;
+ hideOverlay();
+ playStart();
+ lastTime = performance.now();
+ requestAnimationFrame(loop);
+ }
+
+ function endGame() {
+ gameOver = true;
+ running = false;
+ playDie();
+ if (score > best) {
+ best = score;
+ bestEl.textContent = best;
+ try { localStorage.setItem('neonSnakeBest', String(best)); } catch (_) {}
+ }
+ spawnDeathParticles();
+ showOverlay('GAME OVER', 'Press restart or Space', true);
+ }
+
+ function spawnDeathParticles() {
+ const head = snake[0];
+ const hx = head.x * GRID + GRID / 2;
+ const hy = head.y * GRID + GRID / 2;
+ for (let i = 0; i < 40; i++) {
+ const a = Math.random() * Math.PI * 2;
+ const sp = 1 + Math.random() * 4;
+ particles.push({
+ x: hx, y: hy,
+ vx: Math.cos(a) * sp, vy: Math.sin(a) * sp,
+ life: 1, decay: 0.015 + Math.random() * 0.02,
+ r: 2 + Math.random() * 3,
+ color: Math.random() > 0.5 ? '#ff00aa' : '#00ffc8'
+ });
+ }
+ }
+
+ function spawnEatParticles(fx, fy) {
+ const px = fx * GRID + GRID / 2;
+ const py = fy * GRID + GRID / 2;
+ for (let i = 0; i < 14; i++) {
+ const a = Math.random() * Math.PI * 2;
+ const sp = 0.8 + Math.random() * 2.5;
+ particles.push({
+ x: px, y: py,
+ vx: Math.cos(a) * sp, vy: Math.sin(a) * sp,
+ life: 1, decay: 0.025 + Math.random() * 0.02,
+ r: 1.5 + Math.random() * 2.5,
+ color: '#00ffc8'
+ });
+ }
+ }
+
+ function updateParticles(dt) {
+ for (let i = particles.length - 1; i >= 0; i--) {
+ const p = particles[i];
+ p.x += p.vx;
+ p.y += p.vy;
+ p.vx *= 0.98;
+ p.vy *= 0.98;
+ p.life -= p.decay * (dt / 16);
+ if (p.life <= 0) particles.splice(i, 1);
+ }
+ }
+
+ function tick() {
+ dir = nextDir;
+ const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };
+
+ if (head.x < 0 || head.x >= COLS || head.y < 0 || head.y >= ROWS) {
+ endGame();
+ return;
+ }
+ for (let i = 0; i < snake.length; i++) {
+ if (snake[i].x === head.x && snake[i].y === head.y) {
+ endGame();
+ return;
+ }
+ }
+
+ snake.unshift(head);
+
+ if (head.x === food.x && head.y === food.y) {
+ score += 10;
+ scoreEl.textContent = score;
+ spawnEatParticles(food.x, food.y);
+ playEat();
+ spawnFood();
+ if (score % 50 === 0) {
+ speedLevel++;
+ tickMs = Math.max(50, 140 - (speedLevel - 1) * 10);
+ speedEl.textContent = speedLevel;
+ }
+ } else {
+ snake.pop();
+ }
+ }
+
+ function drawGrid() {
+ ctx.strokeStyle = 'rgba(40, 40, 80, 0.35)';
+ ctx.lineWidth = 0.5;
+ for (let x = 0; x <= COLS; x++) {
+ ctx.beginPath();
+ ctx.moveTo(x * GRID, 0);
+ ctx.lineTo(x * GRID, H);
+ ctx.stroke();
+ }
+ for (let y = 0; y <= ROWS; y++) {
+ ctx.beginPath();
+ ctx.moveTo(0, y * GRID);
+ ctx.lineTo(W, y * GRID);
+ ctx.stroke();
+ }
+ }
+
+ function drawRoundedRect(x, y, w, h, r) {
+ ctx.beginPath();
+ ctx.moveTo(x + r, y);
+ ctx.arcTo(x + w, y, x + w, y + h, r);
+ ctx.arcTo(x + w, y + h, x, y + h, r);
+ ctx.arcTo(x, y + h, x, y, r);
+ ctx.arcTo(x, y, x + w, y, r);
+ ctx.closePath();
+ }
+
+ function drawSnake() {
+ const len = snake.length;
+ for (let i = len - 1; i >= 0; i--) {
+ const s = snake[i];
+ const t = i / Math.max(len - 1, 1);
+ const pad = 1.5;
+ const x = s.x * GRID + pad;
+ const y = s.y * GRID + pad;
+ const sz = GRID - pad * 2;
+
+ const r = Math.floor(0 + t * 80);
+ const g = Math.floor(255 - t * 100);
+ const b = Math.floor(200 + t * 55);
+ const alpha = 1 - t * 0.35;
+
+ ctx.save();
+ ctx.shadowColor = `rgba(0, 255, 200, ${0.6 - t * 0.4})`;
+ ctx.shadowBlur = i === 0 ? 18 : 8;
+
+ if (i === 0) {
+ const grad = ctx.createRadialGradient(x + sz / 2, y + sz / 2, 0, x + sz / 2, y + sz / 2, sz);
+ grad.addColorStop(0, '#ffffff');
+ grad.addColorStop(0.3, '#00ffc8');
+ grad.addColorStop(1, '#00aaff');
+ ctx.fillStyle = grad;
+ } else {
+ ctx.fillStyle = `rgba(${r},${g},${b},${alpha})`;
+ }
+
+ drawRoundedRect(x, y, sz, sz, 5);
+ ctx.fill();
+ ctx.restore();
+
+ if (i === 0) {
+ const eyeOffset = 4;
+ const eyeR = 2.2;
+ let ex1, ey1, ex2, ey2;
+ if (dir.x === 1) { ex1 = x + sz - 6; ey1 = y + 5; ex2 = x + sz - 6; ey2 = y + sz - 5; }
+ else if (dir.x === -1) { ex1 = x + 6; ey1 = y + 5; ex2 = x + 6; ey2 = y + sz - 5; }
+ else if (dir.y === -1) { ex1 = x + 5; ey1 = y + 6; ex2 = x + sz - 5; ey2 = y + 6; }
+ else { ex1 = x + 5; ey1 = y + sz - 6; ex2 = x + sz - 5; ey2 = y + sz - 6; }
+
+ ctx.fillStyle = '#0a0a12';
+ ctx.beginPath(); ctx.arc(ex1, ey1, eyeR, 0, Math.PI * 2); ctx.fill();
+ ctx.beginPath(); ctx.arc(ex2, ey2, eyeR, 0, Math.PI * 2); ctx.fill();
+ ctx.fillStyle = '#00ffc8';
+ ctx.beginPath(); ctx.arc(ex1 + dir.x * 0.6, ey1 + dir.y * 0.6, 0.9, 0, Math.PI * 2); ctx.fill();
+ ctx.beginPath(); ctx.arc(ex2 + dir.x * 0.6, ey2 + dir.y * 0.6, 0.9, 0, Math.PI * 2); ctx.fill();
+ }
+ }
+ }
+
+ function drawFood() {
+ foodPulse += 0.06;
+ const pulse = Math.sin(foodPulse) * 0.15 + 0.85;
+ const cx = food.x * GRID + GRID / 2;
+ const cy = food.y * GRID + GRID / 2;
+ const r = (GRID / 2 - 3) * pulse;
+
+ ctx.save();
+ ctx.shadowColor = '#ff00aa';
+ ctx.shadowBlur = 20 + Math.sin(foodPulse) * 8;
+
+ const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, r * 1.4);
+ grad.addColorStop(0, '#ffffff');
+ grad.addColorStop(0.25, '#ff66cc');
+ grad.addColorStop(0.7, '#ff00aa');
+ grad.addColorStop(1, 'rgba(255,0,170,0)');
+ ctx.fillStyle = grad;
+ ctx.beginPath();
+ ctx.arc(cx, cy, r * 1.3, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.shadowBlur = 12;
+ ctx.fillStyle = '#ff00aa';
+ ctx.beginPath();
+ ctx.arc(cx, cy, r * 0.55, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ function drawParticles() {
+ for (const p of particles) {
+ ctx.save();
+ ctx.globalAlpha = Math.max(0, p.life);
+ ctx.shadowColor = p.color;
+ ctx.shadowBlur = 8;
+ ctx.fillStyle = p.color;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, p.r * p.life, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+ }
+
+ function draw() {
+ ctx.fillStyle = '#0d0d1a';
+ ctx.fillRect(0, 0, W, H);
+
+ const bgGrad = ctx.createRadialGradient(W / 2, H / 2, 0, W / 2, H / 2, W * 0.7);
+ bgGrad.addColorStop(0, 'rgba(20, 10, 40, 0.5)');
+ bgGrad.addColorStop(1, 'rgba(10, 10, 26, 0)');
+ ctx.fillStyle = bgGrad;
+ ctx.fillRect(0, 0, W, H);
+
+ drawGrid();
+ if (snake) drawSnake();
+ if (food) drawFood();
+ drawParticles();
+
+ if (paused && running) {
+ ctx.fillStyle = 'rgba(8, 8, 20, 0.55)';
+ ctx.fillRect(0, 0, W, H);
+ ctx.font = '700 28px Orbitron, sans-serif';
+ ctx.fillStyle = '#00ffc8';
+ ctx.textAlign = 'center';
+ ctx.shadowColor = '#00ffc8';
+ ctx.shadowBlur = 15;
+ ctx.fillText('PAUSED', W / 2, H / 2);
+ ctx.shadowBlur = 0;
+ }
+ }
+
+ function loop(now) {
+ if (!running && particles.length === 0) {
+ draw();
+ return;
+ }
+
+ const dt = Math.min(now - lastTime, 50);
+ lastTime = now;
+
+ if (running && !paused && !gameOver) {
+ acc += dt;
+ while (acc >= tickMs) {
+ acc -= tickMs;
+ tick();
+ if (gameOver) break;
+ }
+ }
+
+ updateParticles(dt);
+ draw();
+
+ if (running || particles.length > 0) {
+ requestAnimationFrame(loop);
+ }
+ }
+
+ const keyMap = {
+ ArrowUp: { x: 0, y: -1 }, ArrowDown: { x: 0, y: 1 },
+ ArrowLeft: { x: -1, y: 0 }, ArrowRight: { x: 1, y: 0 },
+ w: { x: 0, y: -1 }, s: { x: 0, y: 1 },
+ a: { x: -1, y: 0 }, d: { x: 1, y: 0 },
+ W: { x: 0, y: -1 }, S: { x: 0, y: 1 },
+ A: { x: -1, y: 0 }, D: { x: 1, y: 0 }
+ };
+
+ document.addEventListener('keydown', function (e) {
+ if (e.key === 'p' || e.key === 'P') {
+ if (running && !gameOver) {
+ paused = !paused;
+ if (!paused) {
+ lastTime = performance.now();
+ requestAnimationFrame(loop);
+ }
+ }
+ e.preventDefault();
+ return;
+ }
+
+ if (e.key === ' ' || e.key === 'Enter') {
+ if (!running || gameOver) startGame();
+ e.preventDefault();
+ return;
+ }
+
+ const nd = keyMap[e.key];
+ if (nd) {
+ if (nd.x !== -dir.x || nd.y !== -dir.y) {
+ nextDir = nd;
+ }
+ e.preventDefault();
+ }
+ });
+
+ startBtn.addEventListener('click', startGame);
+
+ soundBtn.addEventListener('click', function () {
+ soundOn = !soundOn;
+ soundBtn.textContent = soundOn ? '🔊' : '🔇';
+ soundBtn.classList.toggle('off', !soundOn);
+ if (soundOn) ensureAudio();
+ });
+
+ reset();
+ draw();
+ requestAnimationFrame(function anim(now) {
+ lastTime = now;
+ updateParticles(16);
+ foodPulse += 0.06;
+ draw();
+ if (!running) requestAnimationFrame(anim);
+ });
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/673cf6b31c78/grok.png b/data/artifacts/673cf6b31c78/grok.png
new file mode 100644
index 0000000..11e7df3
Binary files /dev/null and b/data/artifacts/673cf6b31c78/grok.png differ
diff --git a/data/artifacts/673cf6b31c78/hermes3-8b.html b/data/artifacts/673cf6b31c78/hermes3-8b.html
new file mode 100644
index 0000000..abec1b7
--- /dev/null
+++ b/data/artifacts/673cf6b31c78/hermes3-8b.html
@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Neon Snake</title>
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ background-color: black;
+ color: white;
+ }
+
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ font-family: 'Arial', sans-serif;
+ }
+
+ #game-container {
+ position: relative;
+ width: 400px;
+ height: 400px;
+ border-radius: 10px;
+ overflow: hidden;
+ box-shadow: 0 0 20px rgba(255, 255, 255, 0.3);
+ }
+
+ .snake {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 10px;
+ height: 10px;
+ background-color: #4CD137;
+ border-radius: 50%;
+ box-shadow: 0 0 20px rgba(76, 209, 55, 0.5);
+ }
+
+ .food {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 10px;
+ height: 10px;
+ background-color: #F79707;
+ border-radius: 50%;
+ box-shadow: 0 0 20px rgba(247, 151, 7, 0.5);
+ }
+
+ .score {
+ margin-top: 20px;
+ font-size: 24px;
+ }
+ </style>
+</head>
+<body>
+ <div id="game-container">
+ <div class="snake"></div>
+ <div class="food"></div>
+ <div class="score" id="score">Score: 0</div>
+ </div>
+
+ <script>
+ const gameContainer = document.getElementById('game-container');
+ let snake;
+ let food;
+ let score = 0;
+
+ // Game configuration
+ const gridSize = 20;
+ const initialSnakeLength = 5;
+ const minFoodDistance = 4;
+
+ function init() {
+ setupCanvas();
+ createSnake(initialSnakeLength);
+ createFood();
+ displayScore();
+
+ document.addEventListener('keydown', changeDirection);
+
+ setInterval(() => gameLoop(), 100);
+ }
+
+ function setupCanvas() {
+ gameContainer.style.gridTemplateColumns = `repeat(${gridSize}, 1fr)`;
+ gameContainer.style.gridTemplateRows = `repeat(${gridSize}, 1fr)`;
+
+ snake = [];
+ for (let i = 0; i < initialSnakeLength; i++) {
+ createSnakePart(i * gridSize / initialSnakeLength, gridSize / initialSnakeLength);
+ }
+ }
+
+ function createSnakePart(x, y) {
+ const part = document.createElement('div');
+ part.className = 'snake';
+ part.style.gridColumnStart = x;
+ part.style.gridRowStart = y;
+ gameContainer.appendChild(part);
+ snake.push(part);
+ }
+
+ function createSnake(initialLength) {
+ for (let i = 0; i < initialLength; i++) {
+ createSnakePart(i, gridSize / 2);
+ }
+ }
+
+ function createFood() {
+ food = document.createElement('div');
+ food.className = 'food';
+ placeRandomly(food);
+
+ gameContainer.appendChild(food);
+ }
+
+ function placeRandomly(element) {
+ const x = Math.floor(Math.random() * gridSize + 1) * gridSize / gridSize;
+ const y = Math.floor(Math.random() * gridSize + 1) * gridSize / gridSize;
+
+ element.style.gridColumnStart = x;
+ element.style.gridRowStart = y;
+ }
+
+ function displayScore() {
+ document.getElementById('score').textContent = `Score: ${score}`;
+ }
+
+ function changeDirection(event) {
+ const key = event.key;
+ let directionX = 0, directionY = 0;
+
+ if (key === 'ArrowUp') directionY = -1;
+ else if (key === 'ArrowDown') directionY = 1;
+ else if (key === 'ArrowLeft') directionX = -1;
+ else if (key === 'ArrowRight') directionX = 1;
+
+ changeSnakeDirection(directionX, directionY);
+ }
+
+ function changeSnakeDirection(x, y) {
+ const head = document.createElement('div');
+ head.className = 'snake';
+ head.style.gridColumnStart = snake[0].style.gridColumnStart - x;
+ head.style.gridRowStart = snake[0].style.gridRowStart - y;
+ gameContainer.appendChild(head);
+ snake.unshift(head);
+
+ if (checkCollision(head, food)) {
+ growSnake(head);
+ score++;
+ displayScore();
+ placeRandomly(food);
+ } else {
+ gameContainer.removeChild(snake.pop());
+ }
+ }
+
+ function growSnake(newHeadPart) {
+ const newBodyPart = document.createElement('div');
+ newBodyPart.className = 'snake';
+ newBodyPart.style.gridColumnStart = snake[0].style.gridColumnStart;
+ newBodyPart.style.gridRowStart = snake[0].style.gridRowStart - 1;
+ gameContainer.appendChild(newBodyPart);
+ snake.push(newBodyPart);
+ }
+
+ function checkCollision(snakeHead, food) {
+ const headPosition = {
+ x: parseInt(snakeHead.style.gridColumnStart),
+ y: parseInt(snakeHead.style.gridRowStart)
+ };
+
+ return (
+ headPosition.x === parseInt(food.style.gridColumnStart) &&
+ headPosition.y === parseInt(food.style.gridRowStart)
+ );
+ }
+
+ function gameLoop() {
+ changeSnakeDirection(0, 1);
+ }
+
+ init();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/673cf6b31c78/hermes3-8b.png b/data/artifacts/673cf6b31c78/hermes3-8b.png
new file mode 100644
index 0000000..7f6234f
Binary files /dev/null and b/data/artifacts/673cf6b31c78/hermes3-8b.png differ
diff --git a/data/artifacts/673cf6b31c78/hf-qwen-coder-32b.html b/data/artifacts/673cf6b31c78/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..6948184
--- /dev/null
+++ b/data/artifacts/673cf6b31c78/hf-qwen-coder-32b.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Neon Snake</title>
+<style>
+body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #000; overflow: hidden; font-family: 'Arial', sans-serif; }
+canvas { border: 2px solid #fff; box-shadow: 0 0 20px rgba(0, 255, 255, 0.8); }
+#score { position: absolute; top: 10px; left: 10px; color: #0ff; font-size: 2em; }
+</style>
+</head>
+<body>
+<canvas id="gameCanvas"></canvas>
+<div id="score">Score: 0</div>
+<script>
+const canvas = document.getElementById('gameCanvas');
+const ctx = canvas.getContext('2d');
+canvas.width = 400;
+canvas.height = 400;
+const gridSize = 20;
+let snake = [{ x: 160, y: 160 }];
+let direction = { x: 0, y: 0 };
+let food = {};
+let score = 0;
+let speed = 250;
+
+function getRandomFoodPosition() {
+ return {
+ x: Math.floor(Math.random() * (canvas.width / gridSize)) * gridSize,
+ y: Math.floor(Math.random() * (canvas.height / gridSize)) * gridSize
+ };
+}
+
+food = getRandomFoodPosition();
+
+document.addEventListener('keydown', e => {
+ switch (e.key) {
+ case 'ArrowUp': if (direction.y === 0) direction = { x: 0, y: -gridSize }; break;
+ case 'ArrowDown': if (direction.y === 0) direction = { x: 0, y: gridSize }; break;
+ case 'ArrowLeft': if (direction.x === 0) direction = { x: -gridSize, y: 0 }; break;
+ case 'ArrowRight': if (direction.x === 0) direction = { x: gridSize, y: 0 }; break;
+ case 'w': if (direction.y === 0) direction = { x: 0, y: -gridSize }; break;
+ case 's': if (direction.y === 0) direction = { x: 0, y: gridSize }; break;
+ case 'a': if (direction.x === 0) direction = { x: -gridSize, y: 0 }; break;
+ case 'd': if (direction.x === 0) direction = { x: gridSize, y: 0 }; break;
+ }
+});
+
+function drawRect(x, y, color) {
+ ctx.fillStyle = color;
+ ctx.fillRect(x, y, gridSize, gridSize);
+}
+
+function updateSnake() {
+ const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };
+ if (head.x === food.x && head.y === food.y) {
+ score++;
+ speed -= 5;
+ food = getRandomFoodPosition();
+ } else {
+ snake.pop();
+ }
+ snake.unshift(head);
+
+ if (head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height || checkCollision(head)) {
+ resetGame();
+ }
+}
+
+function checkCollision(head) {
+ return snake.some((part, index) => index !== 0 && part.x === head.x && part.y === head.y);
+}
+
+function drawFood() {
+ drawRect(food.x, food.y, '#ff0');
+}
+
+function drawSnake() {
+ snake.forEach(part => drawRect(part.x, part.y, '#0ff'));
+}
+
+function resetGame() {
+ alert('Game Over! Score: ' + score);
+ snake = [{ x: 160, y: 160 }];
+ direction = { x: 0, y: 0 };
+ score = 0;
+ speed = 250;
+ food = getRandomFoodPosition();
+}
+
+function gameLoop() {
+ updateSnake();
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ drawFood();
+ drawSnake();
+ document.getElementById('score').textContent = 'Score: ' + score;
+ setTimeout(gameLoop, speed);
+}
+
+gameLoop();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/673cf6b31c78/hf-qwen-coder-32b.png b/data/artifacts/673cf6b31c78/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..2a6ae56
Binary files /dev/null and b/data/artifacts/673cf6b31c78/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/673cf6b31c78/kimi.html b/data/artifacts/673cf6b31c78/kimi.html
new file mode 100644
index 0000000..62bca77
--- /dev/null
+++ b/data/artifacts/673cf6b31c78/kimi.html
@@ -0,0 +1,434 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Neon Snake</title>
+<style>
+* { margin: 0; padding: 0; box-sizing: border-box; }
+body {
+ background: #000;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ font-family: 'Courier New', monospace;
+ overflow: hidden;
+}
+#container {
+ position: relative;
+ box-shadow: 0 0 100px rgba(0, 255, 255, 0.2);
+}
+canvas {
+ display: block;
+ background: radial-gradient(circle at center, #0a0a1a 0%, #000 100%);
+ border: 3px solid #0ff;
+ box-shadow: 0 0 30px #0ff, inset 0 0 50px rgba(0, 255, 255, 0.05);
+}
+#ui {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ color: #0ff;
+ font-size: 28px;
+ font-weight: bold;
+ text-shadow: 0 0 20px #0ff;
+ pointer-events: none;
+ z-index: 10;
+}
+#game-over {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+ color: #f0f;
+ display: none;
+ background: rgba(0, 0, 0, 0.95);
+ padding: 50px;
+ border: 3px solid #f0f;
+ box-shadow: 0 0 50px #f0f, inset 0 0 30px rgba(255, 0, 255, 0.2);
+ z-index: 20;
+}
+#game-over h1 {
+ margin: 0 0 20px 0;
+ font-size: 56px;
+ text-shadow: 0 0 30px #f0f;
+ letter-spacing: 4px;
+}
+#game-over p {
+ font-size: 24px;
+ margin-bottom: 30px;
+ color: #fff;
+ text-shadow: 0 0 10px #fff;
+}
+#game-over button {
+ background: transparent;
+ border: 2px solid #0ff;
+ color: #0ff;
+ padding: 15px 40px;
+ font-size: 24px;
+ cursor: pointer;
+ font-family: inherit;
+ text-transform: uppercase;
+ letter-spacing: 3px;
+ text-shadow: 0 0 10px #0ff;
+ box-shadow: 0 0 15px #0ff;
+ transition: all 0.3s;
+}
+#game-over button:hover {
+ background: #0ff;
+ color: #000;
+ box-shadow: 0 0 30px #0ff, 0 0 60px #0ff;
+ transform: scale(1.05);
+}
+#hint {
+ position: absolute;
+ bottom: -40px;
+ left: 50%;
+ transform: translateX(-50%);
+ color: #0ff;
+ opacity: 0.6;
+ font-size: 14px;
+ text-shadow: 0 0 10px #0ff;
+ white-space: nowrap;
+}
+</style>
+</head>
+<body>
+<div id="container">
+ <canvas id="game" width="700" height="700"></canvas>
+ <div id="ui">SCORE: <span id="score">0</span></div>
+ <div id="game-over">
+ <h1>GAME OVER</h1>
+ <p>FINAL SCORE: <span id="final-score">0</span></p>
+ <button onclick="game.restart()">RESTART</button>
+ </div>
+ <div id="hint">ARROWS / WASD • SPACE to Pause • CLICK for Sound</div>
+</div>
+
+<script>
+const canvas = document.getElementById('game');
+const ctx = canvas.getContext('2d');
+const scoreEl = document.getElementById('score');
+const finalScoreEl = document.getElementById('final-score');
+const gameOverEl = document.getElementById('game-over');
+
+// Audio System
+let audioCtx = null;
+function initAudio() {
+ if (!audioCtx) {
+ audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ }
+ if (audioCtx.state === 'suspended') {
+ audioCtx.resume();
+ }
+}
+
+function playTone(freq, type, duration, vol = 0.3) {
+ if (!audioCtx) return;
+ const osc = audioCtx.createOscillator();
+ const gain = audioCtx.createGain();
+ osc.type = type;
+ osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
+ gain.gain.setValueAtTime(vol, audioCtx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + duration);
+ osc.connect(gain);
+ gain.connect(audioCtx.destination);
+ osc.start();
+ osc.stop(audioCtx.currentTime + duration);
+}
+
+function playEat() {
+ playTone(880, 'sine', 0.1, 0.3);
+ setTimeout(() => playTone(1760, 'sine', 0.2, 0.2), 50);
+}
+
+function playDie() {
+ playTone(400, 'sawtooth', 0.5, 0.4);
+ setTimeout(() => playTone(200, 'sawtooth', 0.5, 0.4), 100);
+ setTimeout(() => playTone(100, 'sawtooth', 0.8, 0.4), 200);
+}
+
+// Game Constants
+const GRID_SIZE = 25;
+const CELL_SIZE = canvas.width / GRID_SIZE;
+const INITIAL_SPEED = 120;
+const MIN_SPEED = 40;
+const SPEED_DECREASE = 2;
+
+class Particle {
+ constructor(x, y, color) {
+ this.x = x;
+ this.y = y;
+ this.vx = (Math.random() - 0.5) * 4;
+ this.vy = (Math.random() - 0.5) * 4;
+ this.life = 1;
+ this.color = color;
+ }
+ update() {
+ this.x += this.vx;
+ this.y += this.vy;
+ this.life -= 0.03;
+ }
+ draw(ctx) {
+ ctx.globalAlpha = this.life;
+ ctx.fillStyle = this.color;
+ ctx.fillRect(this.x, this.y, 3, 3);
+ ctx.globalAlpha = 1;
+ }
+}
+
+class Snake {
+ constructor() {
+ this.reset();
+ }
+
+ reset() {
+ this.body = [{x: 10, y: 12}, {x: 9, y: 12}, {x: 8, y: 12}];
+ this.oldBody = [...this.body];
+ this.direction = {x: 1, y: 0};
+ this.nextDirection = {x: 1, y: 0};
+ this.grow = false;
+ }
+
+ update() {
+ this.oldBody = this.body.map(s => ({...s}));
+ this.direction = this.nextDirection;
+ const head = {
+ x: this.body[0].x + this.direction.x,
+ y: this.body[0].y + this.direction.y
+ };
+
+ if (head.x < 0 || head.x >= GRID_SIZE || head.y < 0 || head.y >= GRID_SIZE) {
+ return false;
+ }
+
+ for (let seg of this.body) {
+ if (head.x === seg.x && head.y === seg.y) return false;
+ }
+
+ this.body.unshift(head);
+ if (!this.grow) this.body.pop();
+ else this.grow = false;
+ return true;
+ }
+
+ setDirection(x, y) {
+ if (x !== -this.direction.x || y !== -this.direction.y) {
+ this.nextDirection = {x, y};
+ }
+ }
+
+ grow() { this.grow = true; }
+
+ draw(ctx, progress) {
+ this.body.forEach((seg, i) => {
+ const old = this.oldBody[i] || seg;
+ const x = old.x + (seg.x - old.x) * progress;
+ const y = old.y + (seg.y - old.y) * progress;
+ const px = x * CELL_SIZE + 2;
+ const py = y * CELL_SIZE + 2;
+ const size = CELL_SIZE - 4;
+
+ const hue = 180 - (i * 3);
+ const color = `hsl(${hue}, 100%, ${i === 0 ? 70 : 50}%)`;
+
+ ctx.shadowBlur = i === 0 ? 25 : 15;
+ ctx.shadowColor = color;
+ ctx.fillStyle = color;
+ ctx.fillRect(px, py, size, size);
+
+ if (i === 0) {
+ ctx.fillStyle = '#000';
+ ctx.shadowBlur = 0;
+ const eyeSize = 4;
+ const ex = px + (this.direction.x === 1 ? 14 : this.direction.x === -1 ? 4 : 9);
+ const ey = py + (this.direction.y === 1 ? 14 : this.direction.y === -1 ? 4 : 9);
+ ctx.fillRect(ex, ey, eyeSize, eyeSize);
+ }
+ });
+ ctx.shadowBlur = 0;
+ }
+}
+
+class Food {
+ constructor() {
+ this.pos = {x: 15, y: 12};
+ this.pulse = 0;
+ }
+
+ randomize(snake) {
+ let valid = false;
+ while (!valid) {
+ this.pos = {
+ x: Math.floor(Math.random() * GRID_SIZE),
+ y: Math.floor(Math.random() * GRID_SIZE)
+ };
+ valid = !snake.body.some(s => s.x === this.pos.x && s.y === this.pos.y);
+ }
+ }
+
+ draw(ctx) {
+ const cx = this.pos.x * CELL_SIZE + CELL_SIZE/2;
+ const cy = this.pos.y * CELL_SIZE + CELL_SIZE/2;
+ const r = CELL_SIZE/2 - 4 + Math.sin(this.pulse) * 3;
+
+ ctx.shadowBlur = 30;
+ ctx.shadowColor = '#f0f';
+ ctx.fillStyle = '#f0f';
+ ctx.beginPath();
+ ctx.arc(cx, cy, r, 0, Math.PI*2);
+ ctx.fill();
+
+ ctx.fillStyle = '#fff';
+ ctx.beginPath();
+ ctx.arc(cx-3, cy-3, 3, 0, Math.PI*2);
+ ctx.fill();
+ ctx.shadowBlur = 0;
+ }
+}
+
+class Game {
+ constructor() {
+ this.snake = new Snake();
+ this.food = new Food();
+ this.particles = [];
+ this.score = 0;
+ this.speed = INITIAL_SPEED;
+ this.lastTime = 0;
+ this.accumulator = 0;
+ this.running = true;
+ this.over = false;
+ this.inputLocked = false;
+
+ this.bindInput();
+ requestAnimationFrame(t => this.loop(t));
+ }
+
+ bindInput() {
+ const handleKey = (e) => {
+ initAudio();
+ if (this.over) return;
+
+ switch(e.key) {
+ case 'ArrowUp': case 'w': case 'W':
+ this.snake.setDirection(0, -1); break;
+ case 'ArrowDown': case 's': case 'S':
+ this.snake.setDirection(0, 1); break;
+ case 'ArrowLeft': case 'a': case 'A':
+ this.snake.setDirection(-1, 0); break;
+ case 'ArrowRight': case 'd': case 'D':
+ this.snake.setDirection(1, 0); break;
+ case ' ':
+ this.running = !this.running; break;
+ }
+ };
+ document.addEventListener('keydown', handleKey);
+ canvas.addEventListener('click', initAudio);
+ }
+
+ spawnParticles(x, y) {
+ for (let i = 0; i < 10; i++) {
+ this.particles.push(new Particle(
+ x * CELL_SIZE + CELL_SIZE/2,
+ y * CELL_SIZE + CELL_SIZE/2,
+ '#f0f'
+ ));
+ }
+ }
+
+ update(dt) {
+ if (!this.running || this.over) return;
+
+ this.accumulator += dt;
+ const moveInterval = this.speed;
+
+ while (this.accumulator >= moveInterval) {
+ const alive = this.snake.update();
+ if (!alive) {
+ this.endGame();
+ return;
+ }
+
+ const head = this.snake.body[0];
+ if (head.x === this.food.pos.x && head.y === this.food.pos.y) {
+ this.snake.grow();
+ this.score += 10;
+ scoreEl.textContent = this.score;
+ this.spawnParticles(head.x, head.y);
+ this.food.randomize(this.snake);
+ this.speed = Math.max(MIN_SPEED, this.speed - SPEED_DECREASE);
+ playEat();
+ }
+
+ this.accumulator -= moveInterval;
+ }
+
+ this.particles = this.particles.filter(p => {
+ p.update();
+ return p.life > 0;
+ });
+
+ this.food.pulse += 0.15;
+ }
+
+ draw() {
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ // Grid
+ ctx.strokeStyle = 'rgba(0, 255, 255, 0.03)';
+ ctx.lineWidth = 1;
+ for (let i = 0; i <= GRID_SIZE; i++) {
+ ctx.beginPath();
+ ctx.moveTo(i*CELL_SIZE, 0);
+ ctx.lineTo(i*CELL_SIZE, canvas.height);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.moveTo(0, i*CELL_SIZE);
+ ctx.lineTo(canvas.width, i*CELL_SIZE);
+ ctx.stroke();
+ }
+
+ const progress = Math.min(1, this.accumulator / this.speed);
+ this.food.draw(ctx);
+ this.snake.draw(ctx, progress);
+ this.particles.forEach(p => p.draw(ctx));
+ }
+
+ loop(timestamp) {
+ const dt = timestamp - this.lastTime;
+ this.lastTime = timestamp;
+
+ this.update(dt);
+ this.draw();
+ requestAnimationFrame(t => this.loop(t));
+ }
+
+ endGame() {
+ this.over = true;
+ this.running = false;
+ playDie();
+ finalScoreEl.textContent = this.score;
+ gameOverEl.style.display = 'block';
+ }
+
+ restart() {
+ this.snake.reset();
+ this.food.randomize(this.snake);
+ this.score = 0;
+ this.speed = INITIAL_SPEED;
+ this.accumulator = 0;
+ this.particles = [];
+ scoreEl.textContent = '0';
+ this.over = false;
+ this.running = true;
+ gameOverEl.style.display = 'none';
+ }
+}
+
+const game = new Game();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/673cf6b31c78/kimi.png b/data/artifacts/673cf6b31c78/kimi.png
new file mode 100644
index 0000000..834bd7d
Binary files /dev/null and b/data/artifacts/673cf6b31c78/kimi.png differ
diff --git a/data/artifacts/673cf6b31c78/qwen25-7b.html b/data/artifacts/673cf6b31c78/qwen25-7b.html
new file mode 100644
index 0000000..54bb9d4
--- /dev/null
+++ b/data/artifacts/673cf6b31c78/qwen25-7b.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Neon Snake</title>
+ <style>
+ body { margin: 0; overflow: hidden; background: rgba(0, 0, 255, 0.9); }
+ canvas { display: block; width: 100%; height: 100%; }
+ </style>
+</head>
+<body>
+ <canvas id="gameCanvas"></canvas>
+ <script>
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ class Snake {
+ constructor() {
+ this.body = [[3, 3], [4, 3]];
+ this.direction = 'Right';
+ }
+
+ update() {
+ const head = { x: this.body[0][0], y: this.body[0][1] };
+ switch (this.direction) {
+ case 'Up': head.y--; break;
+ case 'Down': head.y++; break;
+ case 'Left': head.x--; break;
+ case 'Right': head.x++; break;
+ }
+ if (head.x < 0 || head.x >= canvas.width / 20 || head.y < 0 || head.y >= canvas.height / 20) {
+ gameOver();
+ } else {
+ for (let i = this.body.length - 1; i > 0; i--) {
+ this.body[i] = { x: this.body[i-1][0], y: this.body[i-1][1] };
+ }
+ if (head.x === food.x && head.y === food.y) {
+ score++;
+ this.body.push([this.body[this.body.length - 1][0], this.body[this.body.length - 1][1]]);
+ increaseSpeed();
+ createFood();
+ } else {
+ this.body[0] = { x: head.x, y: head.y };
+ }
+ }
+ }
+
+ draw() {
+ ctx.fillStyle = '#fff';
+ for (const segment of this.body) {
+ ctx.fillRect(segment[0] * 20, segment[1] * 20, 20, 20);
+ }
+ }
+ }
+
+ class Food {
+ constructor() {
+ this.x = Math.floor(Math.random() * canvas.width / 20);
+ this.y = Math.floor(Math.random() * canvas.height / 20);
+ }
+
+ draw() {
+ ctx.fillStyle = '#f00';
+ ctx.fillRect(this.x * 20, this.y * 20, 20, 20);
+ }
+ }
+
+ function createFood() {
+ while (true) {
+ const newFood = { x: Math.floor(Math.random() * canvas.width / 20), y: Math.floor(Math.random() * canvas.height / 20) };
+ if (!snake.body.some(segment => segment.x === newFood.x && segment.y === newFood.y)) break;
+ }
+ food = new Food();
+ }
+
+ let snake, score = 0, speed = 150, food;
+
+ function increaseSpeed() {
+ if (speed > 30) {
+ speed -= 20;
+ }
+ }
+
+ function gameOver() {
+ alert(`Game Over! Score: ${score}`);
+ location.reload();
+ }
+
+ document.addEventListener('keydown', e => {
+ switch (e.key) {
+ case 'ArrowUp': snake.direction = 'Up'; break;
+ case 'ArrowDown': snake.direction = 'Down'; break;
+ case 'ArrowLeft': snake.direction = 'Left'; break;
+ case 'ArrowRight': snake.direction = 'Right'; break;
+ }
+ });
+
+ function gameLoop() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ snake.update();
+ food.draw();
+ snake.draw();
+ setTimeout(gameLoop, speed);
+ }
+
+ snake = new Snake();
+ createFood();
+ gameLoop();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/673cf6b31c78/qwen25-7b.png b/data/artifacts/673cf6b31c78/qwen25-7b.png
new file mode 100644
index 0000000..e6bcaba
Binary files /dev/null and b/data/artifacts/673cf6b31c78/qwen25-7b.png differ
diff --git a/data/artifacts/673cf6b31c78/qwen3-14b.html b/data/artifacts/673cf6b31c78/qwen3-14b.html
new file mode 100644
index 0000000..14b259c
--- /dev/null
+++ b/data/artifacts/673cf6b31c78/qwen3-14b.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Neon Snake</title>
+ <style>
+ * {
+ box-sizing: border-box;
+ }
+ body {
+ margin: 0;
+ background: black;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ color: white;
+ overflow: hidden;
+ }
+ canvas {
+ background: #000;
+ border: 2px solid #0ff;
+ box-shadow: 0 0 20px #0ff, 0 0 40px #0ff;
+ outline: none;
+ }
+ h1 {
+ margin: 10px 0;
+ text-shadow: 0 0 10px #0ff;
+ }
+ #score {
+ margin: 10px 0;
+ font-size: 1.2em;
+ text-shadow: 0 0 8px #0ff;
+ }
+ #game-over {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: rgba(0, 0, 0, 0.8);
+ padding: 20px 40px;
+ border: 2px solid #0ff;
+ border-radius: 10px;
+ text-align: center;
+ color: white;
+ font-size: 1.5em;
+ display: none;
+ text-shadow: 0 0 10px #0ff;
+ }
+ #restart {
+ margin-top: 15px;
+ padding: 10px 20px;
+ font-size: 1em;
+ background: #0ff;
+ color: black;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: background 0.2s;
+ }
+ #restart:hover {
+ background: #ff0;
+ }
+ </style>
+</head>
+<body>
+ <h1>Neon Snake</h1>
+ <div id="score">Score: 0</div>
+ <canvas id="game" width="600" height="600"></canvas>
+ <div id="game-over">
+ <div>Game Over</div>
+ <button id="restart">Restart</button>
+ </div>
+ <audio id="eat-sound" src="data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAgD4AAADyAAAC4gAAAPwAAAAAABwAAgAAAEgAAAAAAQAAABwAAACAAQABAAAAAAEABgAAACQAAAAAAIAAAACAAAAAAAACAAQAAAD8AAAAAAQAAAAoAAAAIAAAAAQAAAAEAAAAHAAAABQAAAAEAAACAAAACAAAAAQAAAEoAAAAIAAAAAQAAAAEAAAAHAAQAAAD8AAACAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAAAQAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAAABwAAAAIAAAAAQAAAAEAAAD8AAAAAIAAAQAAAABwAAAAQAA
\ No newline at end of file
diff --git a/data/artifacts/673cf6b31c78/qwen3-14b.png b/data/artifacts/673cf6b31c78/qwen3-14b.png
new file mode 100644
index 0000000..d3f2890
Binary files /dev/null and b/data/artifacts/673cf6b31c78/qwen3-14b.png differ
diff --git a/data/artifacts/9ba7a5497a50/claude-code.html b/data/artifacts/9ba7a5497a50/claude-code.html
new file mode 100644
index 0000000..1561d70
--- /dev/null
+++ b/data/artifacts/9ba7a5497a50/claude-code.html
@@ -0,0 +1,359 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Clock Gallery</title>
+<style>
+ :root{
+ --bg:#0d1117;
+ --card:#161b24;
+ --card-edge:#232b38;
+ --ink:#e8ecf3;
+ --dim:#59637a;
+ --faint:#2a3242;
+ --accent:#5eead4;
+ --accent2:#f4b860;
+ --red:#f47067;
+ }
+ *{margin:0;padding:0;box-sizing:border-box}
+ html,body{height:100%}
+ body{
+ background:radial-gradient(1200px 600px at 50% -10%, #131a26 0%, var(--bg) 60%);
+ color:var(--ink);
+ font-family:"Helvetica Neue", Helvetica, Arial, system-ui, sans-serif;
+ display:flex;flex-direction:column;align-items:center;justify-content:center;
+ padding:32px 20px;min-height:100vh;
+ }
+ h1{
+ font-size:13px;font-weight:600;letter-spacing:.42em;text-transform:uppercase;
+ color:var(--dim);margin-bottom:28px;text-align:center;
+ }
+ .gallery{
+ display:grid;
+ grid-template-columns:repeat(auto-fit,minmax(270px,1fr));
+ gap:20px;width:100%;max-width:1280px;
+ }
+ .card{
+ background:var(--card);
+ border:1px solid var(--card-edge);
+ border-radius:16px;
+ padding:26px 20px 20px;
+ display:flex;flex-direction:column;align-items:center;justify-content:space-between;
+ min-height:340px;
+ box-shadow:0 12px 32px rgba(0,0,0,.35);
+ }
+ .card .label{
+ margin-top:20px;font-size:11px;font-weight:600;letter-spacing:.3em;
+ text-transform:uppercase;color:var(--dim);
+ }
+ .stage{flex:1;display:flex;align-items:center;justify-content:center;width:100%}
+
+ /* ---------- analog ---------- */
+ .analog svg{width:220px;height:220px}
+ .tick{stroke:var(--faint);stroke-width:2}
+ .tick.major{stroke:var(--dim);stroke-width:3}
+ .hand{stroke-linecap:round}
+ .hand.hour{stroke:var(--ink);stroke-width:6}
+ .hand.minute{stroke:var(--ink);stroke-width:4}
+ .hand.second{stroke:var(--red);stroke-width:1.6}
+ .hub{fill:var(--ink)}
+ .hub2{fill:var(--red)}
+
+ /* ---------- flip ---------- */
+ .flip{display:flex;align-items:center;gap:10px}
+ .fgroup{display:flex;gap:5px}
+ .fcolon{color:var(--dim);font-size:30px;font-weight:300;padding-bottom:6px}
+ .fcard{
+ position:relative;width:36px;height:56px;
+ perspective:280px;
+ font-size:34px;font-weight:600;
+ font-variant-numeric:tabular-nums;
+ }
+ .fcard .half{
+ position:absolute;left:0;width:100%;height:50%;overflow:hidden;
+ background:linear-gradient(#232b38,#1b2230);
+ border:1px solid #2c3546;
+ }
+ .fcard .half span{
+ position:absolute;left:0;width:100%;height:56px;
+ line-height:56px;text-align:center;color:var(--ink);
+ }
+ .fcard .htop{top:0;border-radius:6px 6px 0 0;border-bottom:1px solid #0d1117}
+ .fcard .htop span{top:0}
+ .fcard .hbot{bottom:0;border-radius:0 0 6px 6px;border-top:none}
+ .fcard .hbot span{top:-28px}
+ .fcard .flap{visibility:hidden;z-index:2}
+ .fcard .ftop{top:0;border-radius:6px 6px 0 0;transform-origin:bottom;backface-visibility:hidden}
+ .fcard .ftop span{top:0}
+ .fcard .fbot{bottom:0;border-radius:0 0 6px 6px;transform-origin:top;transform:rotateX(90deg);backface-visibility:hidden}
+ .fcard .fbot span{top:-28px}
+ .fcard.go .flap{visibility:visible}
+ .fcard.go .ftop{animation:flipTop .3s ease-in forwards}
+ .fcard.go .fbot{animation:flipBot .3s ease-out .3s forwards}
+ @keyframes flipTop{from{transform:rotateX(0)}to{transform:rotateX(-90deg)}}
+ @keyframes flipBot{from{transform:rotateX(90deg)}to{transform:rotateX(0)}}
+
+ /* ---------- binary ---------- */
+ .binary{display:flex;align-items:flex-end;gap:14px}
+ .bpair{display:flex;gap:8px}
+ .bcol{display:flex;flex-direction:column;justify-content:flex-end;gap:8px;align-items:center}
+ .dot{
+ width:18px;height:18px;border-radius:50%;
+ background:var(--faint);
+ transition:background .15s, box-shadow .15s;
+ }
+ .dot.on{
+ background:var(--accent);
+ box-shadow:0 0 10px rgba(94,234,212,.55);
+ }
+ .bcol .bl{
+ margin-top:4px;font-size:10px;letter-spacing:.1em;color:var(--dim);font-weight:600;
+ }
+ .bsep{width:4px;height:4px;border-radius:50%;background:var(--dim);margin-bottom:38px}
+
+ /* ---------- word ---------- */
+ .word{
+ display:grid;grid-template-columns:repeat(11,1fr);gap:6px 8px;
+ font-size:16px;font-weight:600;letter-spacing:.05em;
+ }
+ .word span{
+ color:var(--faint);text-align:center;transition:color .4s, text-shadow .4s;
+ user-select:none;
+ }
+ .word span.on{
+ color:var(--accent2);
+ text-shadow:0 0 12px rgba(244,184,96,.45);
+ }
+</style>
+</head>
+<body>
+ <h1>Four Clocks</h1>
+ <div class="gallery">
+
+ <div class="card analog">
+ <div class="stage">
+ <svg viewBox="0 0 200 200" aria-label="analog clock">
+ <circle cx="100" cy="100" r="96" fill="#11161f" stroke="#2c3546" stroke-width="2"/>
+ <g id="ticks"></g>
+ <line id="hHour" class="hand hour" x1="100" y1="100" x2="100" y2="58"/>
+ <line id="hMin" class="hand minute" x1="100" y1="100" x2="100" y2="38"/>
+ <line id="hSec" class="hand second" x1="100" y1="112" x2="100" y2="28"/>
+ <circle class="hub" cx="100" cy="100" r="5"/>
+ <circle class="hub2" cx="100" cy="100" r="2.4"/>
+ </svg>
+ </div>
+ <div class="label">Analog</div>
+ </div>
+
+ <div class="card">
+ <div class="stage">
+ <div class="flip" id="flip"></div>
+ </div>
+ <div class="label">Flip</div>
+ </div>
+
+ <div class="card">
+ <div class="stage">
+ <div class="binary" id="binary"></div>
+ </div>
+ <div class="label">Binary</div>
+ </div>
+
+ <div class="card">
+ <div class="stage">
+ <div class="word" id="word"></div>
+ </div>
+ <div class="label">Word</div>
+ </div>
+
+ </div>
+
+<script>
+(function(){
+ var pad = function(n){ return String(n).padStart(2,'0'); };
+
+ /* ============ ANALOG ============ */
+ var ticks = document.getElementById('ticks');
+ for (var i = 0; i < 60; i++){
+ var major = i % 5 === 0;
+ var a = i * Math.PI / 30;
+ var r1 = major ? 84 : 89, r2 = 93;
+ var l = document.createElementNS('http://www.w3.org/2000/svg','line');
+ l.setAttribute('x1', 100 + r1 * Math.sin(a));
+ l.setAttribute('y1', 100 - r1 * Math.cos(a));
+ l.setAttribute('x2', 100 + r2 * Math.sin(a));
+ l.setAttribute('y2', 100 - r2 * Math.cos(a));
+ l.setAttribute('class', 'tick' + (major ? ' major' : ''));
+ ticks.appendChild(l);
+ }
+ var hHour = document.getElementById('hHour'),
+ hMin = document.getElementById('hMin'),
+ hSec = document.getElementById('hSec');
+ function drawAnalog(){
+ var now = new Date();
+ var ms = now.getMilliseconds(),
+ s = now.getSeconds() + ms/1000,
+ m = now.getMinutes() + s/60,
+ h = (now.getHours() % 12) + m/60;
+ hHour.setAttribute('transform','rotate(' + (h*30) + ' 100 100)');
+ hMin .setAttribute('transform','rotate(' + (m*6) + ' 100 100)');
+ hSec .setAttribute('transform','rotate(' + (s*6) + ' 100 100)');
+ requestAnimationFrame(drawAnalog);
+ }
+ requestAnimationFrame(drawAnalog);
+
+ /* ============ FLIP ============ */
+ var flipRoot = document.getElementById('flip');
+ var flipCards = [];
+ function makeCard(){
+ var c = document.createElement('div');
+ c.className = 'fcard';
+ c.dataset.d = '0';
+ c.innerHTML =
+ '<div class="half htop"><span>0</span></div>' +
+ '<div class="half hbot"><span>0</span></div>' +
+ '<div class="half flap ftop"><span>0</span></div>' +
+ '<div class="half flap fbot"><span>0</span></div>';
+ return c;
+ }
+ for (var g = 0; g < 3; g++){
+ if (g > 0){
+ var col = document.createElement('div');
+ col.className = 'fcolon';
+ col.textContent = ':';
+ flipRoot.appendChild(col);
+ }
+ var grp = document.createElement('div');
+ grp.className = 'fgroup';
+ for (var d = 0; d < 2; d++){
+ var card = makeCard();
+ grp.appendChild(card);
+ flipCards.push(card);
+ }
+ flipRoot.appendChild(grp);
+ }
+ function setFlip(card, digit){
+ if (card.dataset.d === digit) return;
+ var old = card.dataset.d;
+ card.dataset.d = digit;
+ var htop = card.children[0].firstChild,
+ hbot = card.children[1].firstChild,
+ ftop = card.children[2].firstChild,
+ fbot = card.children[3].firstChild;
+ ftop.textContent = old;
+ fbot.textContent = digit;
+ htop.textContent = digit;
+ card.classList.remove('go');
+ void card.offsetWidth;
+ card.classList.add('go');
+ clearTimeout(card._t);
+ card._t = setTimeout(function(){
+ hbot.textContent = digit;
+ card.classList.remove('go');
+ }, 640);
+ }
+
+ /* ============ BINARY ============ */
+ var binRoot = document.getElementById('binary');
+ var colBits = [2,4,3,4,3,4]; /* BCD: Hh Mm Ss */
+ var colLbl = ['H','H','M','M','S','S'];
+ var binCols = [];
+ for (var c2 = 0; c2 < 6; c2++){
+ if (c2 === 2 || c2 === 4){
+ var sep = document.createElement('div');
+ sep.className = 'bsep';
+ binRoot.appendChild(sep);
+ }
+ var col = document.createElement('div');
+ col.className = 'bcol';
+ var dots = [];
+ for (var b = colBits[c2] - 1; b >= 0; b--){
+ var dot = document.createElement('div');
+ dot.className = 'dot';
+ dot.dataset.w = 1 << b;
+ col.appendChild(dot);
+ dots.push(dot);
+ }
+ var lb = document.createElement('div');
+ lb.className = 'bl';
+ lb.textContent = colLbl[c2];
+ col.appendChild(lb);
+ binRoot.appendChild(col);
+ binCols.push(dots);
+ }
+ function drawBinary(str){
+ for (var i = 0; i < 6; i++){
+ var v = +str[i];
+ binCols[i].forEach(function(dot){
+ dot.classList.toggle('on', (v & dot.dataset.w) !== 0);
+ });
+ }
+ }
+
+ /* ============ WORD ============ */
+ var GRID = [
+ 'ITLISASAMPM',
+ 'ACQUARTERDC',
+ 'TWENTYFIVEX',
+ 'HALFSTENFTO',
+ 'PASTERUNINE',
+ 'ONESIXTHREE',
+ 'FOURFIVETWO',
+ 'EIGHTELEVEN',
+ 'SEVENTWELVE',
+ 'TENSEOCLOCK'
+ ];
+ var W = {
+ IT:[0,0,2], IS:[0,3,2], QUARTER:[1,2,7], TWENTY:[2,0,6], MFIVE:[2,6,4],
+ HALF:[3,0,4], MTEN:[3,5,3], TO:[3,9,2], PAST:[4,0,4], NINE:[4,7,4],
+ ONE:[5,0,3], SIX:[5,3,3], THREE:[5,6,5], FOUR:[6,0,4], HFIVE:[6,4,4],
+ TWO:[6,8,3], EIGHT:[7,0,5], ELEVEN:[7,5,6], SEVEN:[8,0,5], TWELVE:[8,5,6],
+ HTEN:[9,0,3], OCLOCK:[9,5,6]
+ };
+ var HOURS = ['TWELVE','ONE','TWO','THREE','FOUR','HFIVE','SIX','SEVEN','EIGHT','NINE','HTEN','ELEVEN'];
+ var MINS = {
+ 0:['OCLOCK'], 5:['MFIVE','PAST'], 10:['MTEN','PAST'], 15:['QUARTER','PAST'],
+ 20:['TWENTY','PAST'], 25:['TWENTY','MFIVE','PAST'], 30:['HALF','PAST'],
+ 35:['TWENTY','MFIVE','TO'], 40:['TWENTY','TO'], 45:['QUARTER','TO'],
+ 50:['MTEN','TO'], 55:['MFIVE','TO']
+ };
+ var wordRoot = document.getElementById('word');
+ var cells = [];
+ GRID.forEach(function(row){
+ var r = [];
+ row.split('').forEach(function(ch){
+ var sp = document.createElement('span');
+ sp.textContent = ch;
+ wordRoot.appendChild(sp);
+ r.push(sp);
+ });
+ cells.push(r);
+ });
+ function drawWord(now){
+ var h = now.getHours(), m = now.getMinutes();
+ var m5 = Math.round(m / 5) * 5;
+ if (m5 === 60){ m5 = 0; h++; }
+ var names = ['IT','IS'].concat(MINS[m5]);
+ names.push(HOURS[(m5 > 30 ? h + 1 : h) % 12]);
+ cells.forEach(function(row){ row.forEach(function(sp){ sp.classList.remove('on'); }); });
+ names.forEach(function(n){
+ var w = W[n];
+ for (var i = 0; i < w[2]; i++) cells[w[0]][w[1] + i].classList.add('on');
+ });
+ }
+
+ /* ============ shared tick ============ */
+ function tick(){
+ var now = new Date();
+ var str = pad(now.getHours()) + pad(now.getMinutes()) + pad(now.getSeconds());
+ for (var i = 0; i < 6; i++) setFlip(flipCards[i], str[i]);
+ drawBinary(str);
+ drawWord(now);
+ }
+ tick();
+ setInterval(tick, 1000);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/9ba7a5497a50/claude-code.png b/data/artifacts/9ba7a5497a50/claude-code.png
new file mode 100644
index 0000000..4118ecf
Binary files /dev/null and b/data/artifacts/9ba7a5497a50/claude-code.png differ
diff --git a/data/artifacts/9ba7a5497a50/gemma3-12b.html b/data/artifacts/9ba7a5497a50/gemma3-12b.html
new file mode 100644
index 0000000..48b642f
--- /dev/null
+++ b/data/artifacts/9ba7a5497a50/gemma3-12b.html
@@ -0,0 +1,182 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Live Clocks</title>
+ <style>
+ body {
+ font-family: sans-serif;
+ background-color: #222;
+ color: #eee;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ }
+
+ .clock {
+ width: 200px;
+ height: 200px;
+ border: 1px solid #444;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+ align-items: center;
+ }
+
+ .analog .hands {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ }
+
+ .analog .hand {
+ position: absolute;
+ top: 50%;
+ transform-origin: top center;
+ background-color: #fff;
+ border-radius: 2px;
+ transform: translateY(-50%);
+ }
+
+ .analog .hour-hand {
+ height: 60%;
+ width: 7%;
+ }
+
+ .analog .minute-hand {
+ height: 80%;
+ width: 4%;
+ }
+
+ .flipclock .digit {
+ display: block;
+ width: 100%;
+ height: 32px; /* Adjust as needed */
+ background-color: #000;
+ color: #fff;
+ font-size: 16px;
+ text-align: center;
+ line-height: 32px; /* Adjust to match height */
+ border-bottom: 1px solid #444;
+ }
+
+ .binary {
+ font-family: monospace;
+ font-size: 16px;
+ }
+
+ .wordclock .dot {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ background-color: #fff;
+ border-radius: 50%;
+ margin: -5px; /* Center dots */
+ }
+
+ .wordclock .time {
+ font-size: 24px;
+ }
+ </style>
+</head>
+<body>
+
+ <div class="clock analog">
+ <div class="hands">
+ <div class="hand hour-hand"></div>
+ <div class="hand minute-hand"></div>
+ <div class="hand second-hand"></div>
+ </div>
+ </div>
+
+ <div class="clock flipclock" id="flipclock">
+ <div class="digit" id="hour1"></div>
+ <div class="digit" id="hour2"></div>
+ <div class="digit" id="minute1"></div>
+ <div class="digit" id="minute2"></div>
+ <div class="digit" id="second1"></div>
+ <div class="digit" id="second2"></div>
+ </div>
+
+ <div class="clock binary" id="binaryclock"></div>
+
+ <div class="clock wordclock" id="wordclock">
+ <div class = "time">IT IS HALF PAST TEN</div>
+ </div>
+
+ <script>
+ function updateAnalogClock() {
+ const now = new Date();
+ let hour = now.getHours();
+ let minute = now.getMinutes();
+ let second = now.getSeconds();
+
+ hour = (hour % 12) || 12; // Convert to 12-hour format
+
+ const hourHand = document.querySelector('.analog .hour-hand');
+ const minuteHand = document.querySelector('.analog .minute-hand');
+ const secondHand = document.querySelector('.analog .second-hand');
+
+ hourHand.style.transform = `rotate(${hour * 30 + (minute / 60) * 30}deg)`;
+ minuteHand.style.transform = `rotate(${minute * 6}deg)`;
+ secondHand.style.transform = `rotate(${second * 6}deg)`;
+ }
+
+ function updateFlipClock() {
+ const now = new Date();
+ let hour = now.getHours();
+ let minute = now.getMinutes();
+ let second = now.getSeconds();
+
+
+ hour = String(hour).padStart(2, '0');
+ minute = String(minute).padStart(2, '0');
+ second = String(second).padStart(2, '0');
+
+ document.getElementById('hour1').textContent = hour[0];
+ document.getElementById('hour2').textContent = hour[1];
+ document.getElementById('minute1').textContent = minute[0];
+ document.getElementById('minute2').textContent = minute[1];
+ document.getElementById('second1').textContent = second[0];
+ document.getElementById('second2').textContent = second[1];
+
+ }
+
+
+ function updateBinaryClock() {
+ const now = new Date();
+ let hour = now.getHours();
+ let minute = now.getMinutes();
+ let second = now.getSeconds();
+
+ hour = (hour < 10) ? '0' + hour : hour;
+ minute = (minute < 10) ? '0' + minute : minute;
+ second = (second < 10) ? '0' + second : second;
+
+ let binaryHour = parseInt(hour, 10).toString(2).padStart(4, '0');
+ let binaryMinute = parseInt(minute, 10).toString(2).padStart(4, '0');
+ let binarySecond = parseInt(second, 10).toString(2).padStart(4, '0');
+
+ const binaryClockDiv = document.getElementById('binaryclock');
+ binaryClockDiv.innerHTML = `<pre>${binaryHour}\n${binaryMinute}\n${binarySecond}</pre>`;
+ }
+
+
+ setInterval(() => {
+ updateAnalogClock();
+ updateFlipClock();
+ updateBinaryClock();
+ }, 1000);
+
+ updateAnalogClock(); // Initial call
+ updateFlipClock();
+ updateBinaryClock();
+
+ </script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/9ba7a5497a50/gemma3-12b.png b/data/artifacts/9ba7a5497a50/gemma3-12b.png
new file mode 100644
index 0000000..b4aa332
Binary files /dev/null and b/data/artifacts/9ba7a5497a50/gemma3-12b.png differ
diff --git a/data/artifacts/9ba7a5497a50/gpt.html b/data/artifacts/9ba7a5497a50/gpt.html
new file mode 100644
index 0000000..75313b1
--- /dev/null
+++ b/data/artifacts/9ba7a5497a50/gpt.html
@@ -0,0 +1,826 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>Multi Clock Gallery</title>
+<style>
+ :root {
+ color-scheme: dark;
+ --bg: #050711;
+ --bg-elevated: #0d1020;
+ --fg: #f5f7ff;
+ --accent: #4f8cff;
+ --accent-soft: rgba(79,140,255,0.25);
+ --muted: #7b86a8;
+ --border: #252a44;
+ --danger: #ff5c7b;
+ --glow: 0 0 25px rgba(79,140,255,0.7);
+ --radius-lg: 18px;
+ --radius-sm: 10px;
+ --shadow-soft: 0 22px 45px rgba(0,0,0,0.75);
+ --clock-size: 230px;
+ --transition-fast: 150ms ease-out;
+ }
+
+ * {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ }
+
+ html, body {
+ height: 100%;
+ }
+
+ body {
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text",
+ "Segoe UI", Roboto, sans-serif;
+ background: radial-gradient(circle at top, #181c38 0, #050711 55%, #000 100%);
+ color: var(--fg);
+ display: flex;
+ align-items: stretch;
+ justify-content: center;
+ }
+
+ .page {
+ width: 100%;
+ max-width: 1200px;
+ padding: 22px clamp(18px, 3vw, 28px) 26px;
+ margin: auto;
+ display: flex;
+ flex-direction: column;
+ gap: 18px;
+ }
+
+ header {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ gap: 12px;
+ padding: 6px 4px 2px;
+ }
+
+ h1 {
+ font-size: clamp(19px, 1.6vw, 22px);
+ letter-spacing: 0.13em;
+ text-transform: uppercase;
+ color: var(--muted);
+ font-weight: 600;
+ }
+
+ header .sub {
+ font-size: 13px;
+ color: var(--muted);
+ opacity: 0.9;
+ }
+
+ .grid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 16px;
+ }
+
+ @media (max-width: 1050px) {
+ .grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ grid-auto-rows: 1fr;
+ }
+ }
+
+ @media (max-width: 680px) {
+ .grid {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+ }
+
+ .card {
+ background: radial-gradient(circle at top left,
+ rgba(106,150,255,0.15) 0,
+ rgba(42,58,120,0.12) 32%,
+ rgba(5,7,17,1) 100%);
+ border-radius: var(--radius-lg);
+ border: 1px solid rgba(116,132,204,0.22);
+ box-shadow: var(--shadow-soft);
+ padding: 15px 14px 16px;
+ position: relative;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .card::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(circle at 20% 0%, rgba(79,140,255,0.18) 0, transparent 52%),
+ radial-gradient(circle at 130% 140%, rgba(255,92,123,0.3) 0, transparent 55%);
+ opacity: 0.85;
+ mix-blend-mode: screen;
+ pointer-events: none;
+ }
+
+ .card-inner {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ height: 100%;
+ }
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ gap: 8px;
+ }
+
+ .label {
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 0.18em;
+ color: var(--muted);
+ font-weight: 600;
+ }
+
+ .hint {
+ font-size: 11px;
+ color: var(--muted);
+ opacity: 0.85;
+ }
+
+ .clock-shell {
+ background: radial-gradient(circle at 50% -18%,
+ rgba(255,255,255,0.12) 0,
+ rgba(10,12,32,0.95) 46%,
+ rgba(0,0,0,0.95) 100%);
+ border-radius: calc(var(--radius-lg) - 4px);
+ border: 1px solid rgba(177,190,255,0.17);
+ padding: 18px 14px 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ overflow: hidden;
+ }
+
+ .clock-shell::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(circle at 20% 0%, rgba(79,140,255,0.25) 0, transparent 55%),
+ radial-gradient(circle at 80% 110%, rgba(255,92,123,0.22) 0, transparent 55%);
+ opacity: 0.55;
+ mix-blend-mode: soft-light;
+ pointer-events: none;
+ }
+
+ .clock-shell-inner {
+ position: relative;
+ z-index: 1;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ }
+
+ /* Analog clock */
+
+ .analog-wrapper {
+ width: var(--clock-size);
+ height: var(--clock-size);
+ max-width: 100%;
+ max-height: calc(100vw - 80px);
+ aspect-ratio: 1/1;
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .analog-face {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ background:
+ radial-gradient(circle at 30% 18%, rgba(255,255,255,0.32) 0, transparent 35%),
+ radial-gradient(circle at 50% 150%, rgba(0,0,0,1) 0, #02030a 60%, #000 100%);
+ box-shadow:
+ 0 0 25px rgba(0,0,0,0.85),
+ 0 0 0 1px rgba(255,255,255,0.15),
+ inset 0 0 35px rgba(0,0,0,0.95),
+ inset 0 0 0 1px rgba(102,126,255,0.22);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .analog-center {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ background: #f9fafb;
+ box-shadow: 0 0 12px rgba(255,255,255,0.65);
+ position: absolute;
+ z-index: 5;
+ }
+
+ .analog-hand {
+ position: absolute;
+ bottom: 50%;
+ left: 50%;
+ transform-origin: bottom center;
+ transform: translateX(-50%) rotate(0deg);
+ border-radius: 999px;
+ }
+
+ .analog-hand.hour {
+ width: 6px;
+ height: 30%;
+ background: linear-gradient(to top, #f1f5ff, #9fb4ff);
+ box-shadow: 0 0 12px rgba(159,180,255,0.85);
+ z-index: 3;
+ }
+
+ .analog-hand.minute {
+ width: 4px;
+ height: 40%;
+ background: linear-gradient(to top, #dfe5ff, #5c7fff);
+ box-shadow: 0 0 18px rgba(92,127,255,0.9);
+ z-index: 2;
+ }
+
+ .analog-hand.second {
+ width: 2px;
+ height: 46%;
+ background: linear-gradient(to top, #ff5c7b, #ffb1c1);
+ box-shadow: 0 0 20px rgba(255,92,123,0.9);
+ z-index: 4;
+ }
+
+ .analog-mark {
+ position: absolute;
+ inset: 8%;
+ border-radius: 50%;
+ }
+
+ .analog-mark span {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ width: 2px;
+ height: 8px;
+ background: rgba(198,208,255,0.85);
+ transform-origin: center calc(100% + 40%);
+ border-radius: 999px;
+ opacity: 0.5;
+ }
+
+ .analog-mark span.major {
+ width: 3px;
+ height: 12px;
+ opacity: 0.9;
+ box-shadow: 0 0 10px rgba(198,208,255,0.9);
+ }
+
+ .analog-hour-numeral {
+ position: absolute;
+ inset: 16%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: rgba(223,230,255,0.8);
+ font-size: 13px;
+ font-weight: 600;
+ text-shadow: 0 0 7px rgba(223,230,255,0.9);
+ }
+
+ /* Flip clock */
+
+ .flip-root {
+ display: flex;
+ gap: 6px;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ }
+
+ .flip-group {
+ display: flex;
+ gap: 6px;
+ }
+
+ .flip-colon {
+ font-size: 34px;
+ color: var(--accent);
+ text-shadow: var(--glow);
+ padding: 0 2px 2px;
+ }
+
+ .flip-unit {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ align-items: stretch;
+ }
+
+ .flip-label {
+ font-size: 10px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--muted);
+ text-align: center;
+ }
+
+ .flip-digit {
+ position: relative;
+ width: 42px;
+ height: 60px;
+ border-radius: 9px;
+ box-shadow:
+ 0 16px 25px rgba(0,0,0,0.75),
+ 0 0 0 1px rgba(117,134,236,0.4);
+ background: linear-gradient(145deg, #050713, #141735);
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .flip-card {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-variant-numeric: tabular-nums;
+ font-weight: 600;
+ font-size: 32px;
+ color: #f8f9ff;
+ text-shadow: 0 0 16px rgba(255,255,255,0.65);
+ }
+
+ /* Binary clock */
+
+ .binary-root {
+ display: grid;
+ grid-template-columns: repeat(6, minmax(0, 1fr));
+ gap: 8px;
+ width: 100%;
+ max-width: 260px;
+ }
+
+ .binary-column {
+ display: flex;
+ flex-direction: column-reverse;
+ gap: 4px;
+ align-items: center;
+ }
+
+ .binary-dot {
+ width: 18px;
+ height: 18px;
+ border-radius: 999px;
+ background: radial-gradient(circle at 30% 20%, #1a203a 0, #040614 60%, #000 100%);
+ border: 1px solid rgba(114,129,201,0.55);
+ box-shadow: 0 4px 9px rgba(0,0,0,0.9);
+ position: relative;
+ transition: background var(--transition-fast),
+ box-shadow var(--transition-fast),
+ transform var(--transition-fast),
+ border-color var(--transition-fast),
+ opacity 120ms ease-out;
+ opacity: 0.4;
+ }
+
+ .binary-dot.on {
+ background:
+ radial-gradient(circle at 28% 20%, #fdfdff 0, #8fa5ff 40%, #3658ff 70%, #0c1022 100%);
+ border-color: rgba(159,180,255,0.88);
+ box-shadow:
+ 0 0 16px rgba(108,132,255,0.9),
+ 0 0 32px rgba(108,132,255,0.7);
+ transform: translateY(-1px);
+ opacity: 1;
+ }
+
+ .binary-dot.on.sec {
+ background:
+ radial-gradient(circle at 28% 20%, #fff7fb 0, #ffc2d4 38%, #ff5c7b 70%, #221018 100%);
+ border-color: rgba(255,146,173,0.95);
+ box-shadow:
+ 0 0 16px rgba(255,140,173,0.95),
+ 0 0 30px rgba(255,92,123,0.9);
+ }
+
+ .binary-label {
+ font-size: 10px;
+ color: var(--muted);
+ opacity: 0.85;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ margin-top: 3px;
+ }
+
+ /* Word clock */
+
+ .word-root {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ width: 100%;
+ max-width: 260px;
+ }
+
+ .word-grid {
+ display: grid;
+ grid-template-columns: repeat(11, minmax(0, 1fr));
+ gap: 4px;
+ font-size: 14px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ font-weight: 500;
+ }
+
+ .word-cell {
+ color: rgba(87,98,151,0.95);
+ text-align: center;
+ transition: color 140ms ease-out,
+ text-shadow 140ms ease-out,
+ transform 140ms ease-out;
+ transform-origin: center;
+ }
+
+ .word-cell.on {
+ color: #f8f9ff;
+ text-shadow:
+ 0 0 10px rgba(174,191,255,0.9),
+ 0 0 22px rgba(79,140,255,0.85);
+ transform: translateY(-0.5px);
+ }
+
+ .word-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 11px;
+ color: var(--muted);
+ }
+
+ .word-now {
+ color: rgba(217,222,255,0.95);
+ font-variant-numeric: tabular-nums;
+ }
+</style>
+</head>
+<body>
+<div class="page">
+ <header>
+ <h1>Realtime Clock Gallery</h1>
+ <div class="sub">Four visualizations of the same moment in time</div>
+ </header>
+
+ <main class="grid">
+ <!-- Analog Clock -->
+ <section class="card">
+ <div class="card-inner">
+ <div class="card-header">
+ <div class="label">Analog</div>
+ <div class="hint">Continuous smooth hands</div>
+ </div>
+ <div class="clock-shell">
+ <div class="clock-shell-inner">
+ <div class="analog-wrapper" aria-label="Analog clock">
+ <div class="analog-face">
+ <div class="analog-mark" id="analog-marks"></div>
+ <div class="analog-center"></div>
+ <div class="analog-hand hour" id="hand-hour"></div>
+ <div class="analog-hand minute" id="hand-minute"></div>
+ <div class="analog-hand second" id="hand-second"></div>
+ <!-- 12, 3, 6, 9 numerals -->
+ <div class="analog-hour-numeral" style="transform: rotate(0deg) translateY(-40%);">12</div>
+ <div class="analog-hour-numeral" style="transform: rotate(90deg) translateX(40%);">3</div>
+ <div class="analog-hour-numeral" style="transform: rotate(180deg) translateY(40%);">6</div>
+ <div class="analog-hour-numeral" style="transform: rotate(270deg) translateX(-40%);">9</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <!-- Flip Clock -->
+ <section class="card">
+ <div class="card-inner">
+ <div class="card-header">
+ <div class="label">Flip</div>
+ <div class="hint">Digital split-flap layout</div>
+ </div>
+ <div class="clock-shell">
+ <div class="clock-shell-inner">
+ <div class="flip-root" aria-label="Digital flip clock">
+ <div class="flip-unit">
+ <div class="flip-label">Hours</div>
+ <div class="flip-group">
+ <div class="flip-digit"><div class="flip-card" id="flip-h1">0</div></div>
+ <div class="flip-digit"><div class="flip-card" id="flip-h2">0</div></div>
+ </div>
+ </div>
+ <div class="flip-colon">:</div>
+ <div class="flip-unit">
+ <div class="flip-label">Minutes</div>
+ <div class="flip-group">
+ <div class="flip-digit"><div class="flip-card" id="flip-m1">0</div></div>
+ <div class="flip-digit"><div class="flip-card" id="flip-m2">0</div></div>
+ </div>
+ </div>
+ <div class="flip-colon">:</div>
+ <div class="flip-unit">
+ <div class="flip-label">Seconds</div>
+ <div class="flip-group">
+ <div class="flip-digit"><div class="flip-card" id="flip-s1">0</div></div>
+ <div class="flip-digit"><div class="flip-card" id="flip-s2">0</div></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <!-- Binary Clock -->
+ <section class="card">
+ <div class="card-inner">
+ <div class="card-header">
+ <div class="label">Binary</div>
+ <div class="hint">Columns = H H : M M : S S</div>
+ </div>
+ <div class="clock-shell">
+ <div class="clock-shell-inner">
+ <div class="binary-root" aria-label="Binary clock" id="binary-root"></div>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <!-- Word Clock -->
+ <section class="card">
+ <div class="card-inner">
+ <div class="card-header">
+ <div class="label">Word</div>
+ <div class="hint">Human-friendly rounded time</div>
+ </div>
+ <div class="clock-shell">
+ <div class="clock-shell-inner">
+ <div class="word-root" aria-label="Word clock">
+ <div class="word-grid" id="word-grid"></div>
+ <div class="word-footer">
+ <div>It is approximately:</div>
+ <div class="word-now" id="word-now"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ </main>
+</div>
+
+<script>
+(function() {
+ // Analog marks
+ const marksContainer = document.getElementById('analog-marks');
+ if (marksContainer) {
+ const frag = document.createDocumentFragment();
+ for (let i = 0; i < 60; i++) {
+ const span = document.createElement('span');
+ const angle = i * 6;
+ span.style.transform = `rotate(${angle}deg) translateX(-50%)`;
+ if (i % 5 === 0) span.classList.add('major');
+ frag.appendChild(span);
+ }
+ marksContainer.appendChild(frag);
+ }
+
+ // Binary clock setup (6 columns, 6 bits each)
+ const binaryRoot = document.getElementById('binary-root');
+ const binaryCols = [];
+ if (binaryRoot) {
+ const frag = document.createDocumentFragment();
+ for (let c = 0; c < 6; c++) {
+ const col = document.createElement('div');
+ col.className = 'binary-column';
+ const label = document.createElement('div');
+ label.className = 'binary-label';
+ label.textContent = c < 2 ? 'H' : c < 4 ? 'M' : 'S';
+ const dots = [];
+ for (let r = 0; r < 6; r++) {
+ const dot = document.createElement('div');
+ dot.className = 'binary-dot';
+ if (c >= 4) dot.classList.add('sec');
+ dots.push(dot);
+ col.appendChild(dot);
+ }
+ col.appendChild(label);
+ binaryCols.push(dots);
+ frag.appendChild(col);
+ }
+ binaryRoot.appendChild(frag);
+ }
+
+ // Word clock setup
+ const wordLayout = [
+ "ITLISBHALFTEN",
+ "QUARTERFIVEW",
+ "TWENTYFIVEXX",
+ "MINUTESTOXXX",
+ "PASTTWELVEXX",
+ "ONETHREEFOUR",
+ "FIVESIXSEVEN",
+ "EIGHTNINETEN",
+ "ELEVENOCLOCK"
+ ];
+ const wordMap = {};
+ function addWord(row, colStart, length, key) {
+ for (let i = 0; i < length; i++) {
+ const idx = row * 11 + colStart + i;
+ if (!wordMap[key]) wordMap[key] = [];
+ wordMap[key].push(idx);
+ }
+ }
+ // Define words (indices based on layout above)
+ addWord(0, 0, 2, 'IT');
+ addWord(0, 3, 2, 'IS');
+ addWord(0, 4, 1, 'IS'); // strengthen highlight
+ addWord(0, 5, 4, 'HALF');
+ addWord(0, 9, 3, 'TEN_MIN');
+ addWord(1, 0, 7, 'QUARTER');
+ addWord(1, 7, 4, 'FIVE_MIN');
+ addWord(2, 0, 6, 'TWENTY');
+ addWord(2, 6, 4, 'FIVE_MIN2');
+ addWord(3, 0, 7, 'MINUTES');
+ addWord(3, 7, 2, 'TO');
+ addWord(4, 0, 4, 'PAST');
+ addWord(4, 4, 6, 'TWELVE');
+ addWord(5, 0, 3, 'ONE');
+ addWord(5, 3, 5, 'THREE');
+ addWord(5, 8, 4, 'FOUR');
+ addWord(6, 0, 4, 'FIVE_H');
+ addWord(6, 4, 3, 'SIX');
+ addWord(6, 7, 5, 'SEVEN');
+ addWord(7, 0, 5, 'EIGHT');
+ addWord(7, 5, 4, 'NINE');
+ addWord(7, 9, 3, 'TEN_H');
+ addWord(8, 0, 6, 'ELEVEN');
+ addWord(8, 6, 6, 'OCLOCK'); // goes slightly over but grid bounds handle
+ const wordGridEl = document.getElementById('word-grid');
+ const wordCells = [];
+ if (wordGridEl) {
+ const frag = document.createDocumentFragment();
+ const flat = wordLayout.join('');
+ for (let i = 0; i < flat.length; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'word-cell';
+ cell.textContent = flat[i];
+ frag.appendChild(cell);
+ wordCells.push(cell);
+ }
+ wordGridEl.appendChild(frag);
+ }
+
+ const handH = document.getElementById('hand-hour');
+ const handM = document.getElementById('hand-minute');
+ const handS = document.getElementById('hand-second');
+ const flipIds = ['flip-h1','flip-h2','flip-m1','flip-m2','flip-s1','flip-s2'];
+ const flips = flipIds.map(id => document.getElementById(id));
+ const wordNow = document.getElementById('word-now');
+
+ function setWordHighlight(words) {
+ for (const cell of wordCells) cell.classList.remove('on');
+ // IT IS always on
+ ['IT','IS'].forEach(k => (wordMap[k] || []).forEach(i => wordCells[i].classList.add('on')));
+ words.forEach(k => (wordMap[k] || []).forEach(i => wordCells[i].classList.add('on')));
+ }
+
+ function updateWordClock(now) {
+ const m = now.getMinutes();
+ const hRaw = now.getHours();
+ const rounded = Math.round(m / 5) * 5;
+ let displayM = rounded;
+ let hour = hRaw;
+ let words = [];
+ if (displayM === 60) {
+ displayM = 0;
+ hour = (hour + 1) % 24;
+ }
+ let rel = '';
+ if (displayM === 0) {
+ words.push('OCLOCK');
+ } else {
+ if (displayM === 5) words.push('FIVE_MIN');
+ else if (displayM === 10) words.push('TEN_MIN');
+ else if (displayM === 15) words.push('QUARTER');
+ else if (displayM === 20) words.push('TWENTY');
+ else if (displayM === 25) { words.push('TWENTY','FIVE_MIN2'); }
+ else if (displayM === 30) words.push('HALF');
+ else if (displayM === 35) { words.push('TWENTY','FIVE_MIN2'); rel = 'TO'; hour++; }
+ else if (displayM === 40) { words.push('TWENTY'); rel = 'TO'; hour++; }
+ else if (displayM === 45) { words.push('QUARTER'); rel = 'TO'; hour++; }
+ else if (displayM === 50) { words.push('TEN_MIN'); rel = 'TO'; hour++; }
+ else if (displayM === 55) { words.push('FIVE_MIN'); rel = 'TO'; hour++; }
+ if (!rel && displayM !== 30) rel = 'PAST';
+ if (rel === 'PAST') words.push('PAST');
+ if (rel === 'TO') words.push('TO');
+ }
+ hour = (hour + 24) % 24;
+ let h12 = hour % 12;
+ if (h12 === 0) h12 = 12;
+ const hourWordMap = {
+ 1: 'ONE',
+ 2: 'TWO', // no explicit TWO in grid, will be unlit but phrase still readable
+ 3: 'THREE',
+ 4: 'FOUR',
+ 5: 'FIVE_H',
+ 6: 'SIX',
+ 7: 'SEVEN',
+ 8: 'EIGHT',
+ 9: 'NINE',
+ 10: 'TEN_H',
+ 11: 'ELEVEN',
+ 12: 'TWELVE'
+ };
+ const hw = hourWordMap[h12];
+ if (hw) words.push(hw);
+ setWordHighlight(words);
+ if (wordNow) {
+ const pad = n => String(n).padStart(2,'0');
+ wordNow.textContent = `${pad(now.getHours())}:${pad(now.getMinutes())}`;
+ }
+ }
+
+ function updateBinary(h, m, s) {
+ const vals = [
+ Math.floor(h / 10), h % 10,
+ Math.floor(m / 10), m % 10,
+ Math.floor(s / 10), s % 10
+ ];
+ for (let c = 0; c < 6; c++) {
+ const v = vals[c];
+ const bits = binaryCols[c];
+ for (let b = 0; b < bits.length; b++) {
+ const mask = 1 << b;
+ bits[b].classList.toggle('on', (v & mask) !== 0);
+ }
+ }
+ }
+
+ function updateFlip(h, m, s) {
+ const pad2 = n => String(n).padStart(2,'0');
+ const hs = pad2(h), ms = pad2(m), ss = pad2(s);
+ const values = [hs[0],hs[1],ms[0],ms[1],ss[0],ss[1]];
+ for (let i = 0; i < flips.length; i++) {
+ if (flips[i] && flips[i].textContent !== values[i]) {
+ flips[i].textContent = values[i];
+ }
+ }
+ }
+
+ function updateAnalog(now) {
+ if (!handH || !handM || !handS) return;
+ const ms = now.getMilliseconds();
+ const s = now.getSeconds() + ms / 1000;
+ const m = now.getMinutes() + s / 60;
+ const h = (now.getHours() % 12) + m / 60;
+ const degS = s * 6;
+ const degM = m * 6;
+ const degH = h * 30;
+ handS.style.transform = `translateX(-50%) rotate(${degS}deg)`;
+ handM.style.transform = `translateX(-50%) rotate(${degM}deg)`;
+ handH.style.transform = `translateX(-50%) rotate(${degH}deg)`;
+ }
+
+ function tick() {
+ const now = new Date();
+ const h = now.getHours();
+ const m = now.getMinutes();
+ const s = now.getSeconds();
+ updateAnalog(now);
+ updateFlip(h,m,s);
+ updateBinary(h,m,s);
+ updateWordClock(now);
+ }
+
+ tick();
+ setInterval(tick, 1000);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/9ba7a5497a50/gpt.png b/data/artifacts/9ba7a5497a50/gpt.png
new file mode 100644
index 0000000..839eff7
Binary files /dev/null and b/data/artifacts/9ba7a5497a50/gpt.png differ
diff --git a/data/artifacts/9ba7a5497a50/grok.html b/data/artifacts/9ba7a5497a50/grok.html
new file mode 100644
index 0000000..64fec79
--- /dev/null
+++ b/data/artifacts/9ba7a5497a50/grok.html
@@ -0,0 +1,665 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Clock Gallery</title>
+<style>
+ :root {
+ --bg: #0d0f14;
+ --surface: #161a22;
+ --border: #2a3040;
+ --text: #e8ecf4;
+ --muted: #6b7289;
+ --accent: #6ee7b7;
+ --accent2: #93c5fd;
+ --flip-bg: #1a1f2e;
+ --flip-face: #0f1219;
+ --led-off: #1e2433;
+ --led-on: #6ee7b7;
+ }
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+ body {
+ min-height: 100vh;
+ background: var(--bg);
+ color: var(--text);
+ font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 24px;
+ gap: 28px;
+ }
+ h1 {
+ font-weight: 300;
+ font-size: 1.25rem;
+ letter-spacing: 0.35em;
+ text-transform: uppercase;
+ color: var(--muted);
+ }
+ .gallery {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 20px;
+ width: 100%;
+ max-width: 1280px;
+ }
+ @media (max-width: 1100px) {
+ .gallery { grid-template-columns: repeat(2, 1fr); }
+ }
+ @media (max-width: 560px) {
+ .gallery { grid-template-columns: 1fr; }
+ }
+ .card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 16px;
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 16px;
+ min-height: 320px;
+ }
+ .card h2 {
+ font-size: 0.7rem;
+ font-weight: 600;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ color: var(--muted);
+ }
+ .stage {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ }
+
+ /* Analog */
+ .analog-wrap {
+ position: relative;
+ width: 200px;
+ height: 200px;
+ }
+ .analog-wrap canvas {
+ width: 100%;
+ height: 100%;
+ display: block;
+ }
+
+ /* Flip clock */
+ .flip-clock {
+ display: flex;
+ gap: 6px;
+ align-items: center;
+ }
+ .flip-group {
+ display: flex;
+ gap: 3px;
+ }
+ .flip-digit {
+ position: relative;
+ width: 42px;
+ height: 64px;
+ perspective: 200px;
+ font-variant-numeric: tabular-nums;
+ }
+ .flip-digit .base {
+ position: absolute;
+ inset: 0;
+ background: var(--flip-face);
+ border-radius: 6px;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.4);
+ }
+ .flip-digit .half {
+ height: 50%;
+ display: flex;
+ justify-content: center;
+ overflow: hidden;
+ background: var(--flip-bg);
+ }
+ .flip-digit .half.top {
+ align-items: flex-end;
+ border-radius: 6px 6px 0 0;
+ border-bottom: 1px solid #000;
+ }
+ .flip-digit .half.bottom {
+ align-items: flex-start;
+ border-radius: 0 0 6px 6px;
+ }
+ .flip-digit .half span {
+ font-size: 48px;
+ font-weight: 700;
+ line-height: 64px;
+ color: var(--text);
+ font-family: "SF Mono", "Cascadia Code", "Consolas", monospace;
+ }
+ .flip-digit .half.bottom span {
+ transform: translateY(-50%);
+ }
+ .flip-digit .half.top span {
+ transform: translateY(50%);
+ }
+ .flip-colon {
+ font-size: 36px;
+ font-weight: 700;
+ color: var(--accent);
+ padding: 0 2px;
+ animation: blink 1s steps(1) infinite;
+ }
+ @keyframes blink {
+ 50% { opacity: 0.25; }
+ }
+ .flip-card {
+ position: absolute;
+ left: 0; width: 100%;
+ height: 50%;
+ transform-style: preserve-3d;
+ transform-origin: bottom;
+ backface-visibility: hidden;
+ pointer-events: none;
+ }
+ .flip-card.top-flip {
+ top: 0;
+ transform-origin: bottom;
+ border-radius: 6px 6px 0 0;
+ overflow: hidden;
+ background: var(--flip-bg);
+ border-bottom: 1px solid #000;
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+ }
+ .flip-card.bottom-flip {
+ top: 50%;
+ transform-origin: top;
+ border-radius: 0 0 6px 6px;
+ overflow: hidden;
+ background: var(--flip-bg);
+ display: flex;
+ align-items: flex-start;
+ justify-content: center;
+ }
+ .flip-card span {
+ font-size: 48px;
+ font-weight: 700;
+ line-height: 64px;
+ color: var(--text);
+ font-family: "SF Mono", "Cascadia Code", "Consolas", monospace;
+ }
+ .flip-card.top-flip span { transform: translateY(50%); }
+ .flip-card.bottom-flip span { transform: translateY(-50%); }
+ .flip-card.flipping-top {
+ animation: flipTop 0.3s ease-in forwards;
+ z-index: 2;
+ }
+ .flip-card.flipping-bottom {
+ animation: flipBottom 0.3s ease-out forwards;
+ z-index: 2;
+ }
+ @keyframes flipTop {
+ 0% { transform: rotateX(0deg); }
+ 100% { transform: rotateX(-90deg); }
+ }
+ @keyframes flipBottom {
+ 0% { transform: rotateX(90deg); }
+ 100% { transform: rotateX(0deg); }
+ }
+
+ /* Binary */
+ .binary-clock {
+ display: flex;
+ gap: 14px;
+ }
+ .bin-col {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ }
+ .bin-label {
+ font-size: 0.6rem;
+ letter-spacing: 0.15em;
+ color: var(--muted);
+ text-transform: uppercase;
+ }
+ .bin-bits {
+ display: flex;
+ flex-direction: column-reverse;
+ gap: 6px;
+ }
+ .led {
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: var(--led-off);
+ box-shadow: inset 0 1px 3px rgba(0,0,0,0.5);
+ transition: background 0.15s, box-shadow 0.15s;
+ }
+ .led.on {
+ background: var(--led-on);
+ box-shadow: 0 0 10px rgba(110, 231, 183, 0.55), 0 0 2px var(--led-on);
+ }
+ .bin-value {
+ font-size: 0.75rem;
+ font-family: "SF Mono", Consolas, monospace;
+ color: var(--accent2);
+ margin-top: 4px;
+ }
+
+ /* Word clock */
+ .word-clock {
+ display: grid;
+ grid-template-columns: repeat(11, 1fr);
+ gap: 3px 2px;
+ width: 100%;
+ max-width: 260px;
+ font-family: "Segoe UI", system-ui, sans-serif;
+ font-size: clamp(0.55rem, 1.4vw, 0.72rem);
+ font-weight: 600;
+ letter-spacing: 0.04em;
+ user-select: none;
+ }
+ .word-clock span {
+ text-align: center;
+ color: #2a3142;
+ padding: 3px 0;
+ transition: color 0.3s, text-shadow 0.3s;
+ }
+ .word-clock span.lit {
+ color: var(--accent);
+ text-shadow: 0 0 8px rgba(110, 231, 183, 0.4);
+ }
+</style>
+</head>
+<body>
+ <h1>Clock Gallery</h1>
+ <div class="gallery">
+ <div class="card">
+ <h2>Analog</h2>
+ <div class="stage">
+ <div class="analog-wrap"><canvas id="analog" width="400" height="400"></canvas></div>
+ </div>
+ </div>
+ <div class="card">
+ <h2>Flip</h2>
+ <div class="stage">
+ <div class="flip-clock" id="flip"></div>
+ </div>
+ </div>
+ <div class="card">
+ <h2>Binary</h2>
+ <div class="stage">
+ <div class="binary-clock" id="binary"></div>
+ </div>
+ </div>
+ <div class="card">
+ <h2>Word</h2>
+ <div class="stage">
+ <div class="word-clock" id="word"></div>
+ </div>
+ </div>
+ </div>
+<script>
+(function () {
+ 'use strict';
+
+ /* ========== ANALOG ========== */
+ const aCanvas = document.getElementById('analog');
+ const actx = aCanvas.getContext('2d');
+ const AR = aCanvas.width / 2;
+
+ function drawAnalog(now) {
+ const cx = AR, cy = AR;
+ const r = AR - 12;
+ actx.clearRect(0, 0, aCanvas.width, aCanvas.height);
+
+ // Face
+ actx.beginPath();
+ actx.arc(cx, cy, r, 0, Math.PI * 2);
+ actx.fillStyle = '#0f1219';
+ actx.fill();
+ actx.strokeStyle = '#2a3040';
+ actx.lineWidth = 3;
+ actx.stroke();
+
+ // Ticks
+ for (let i = 0; i < 60; i++) {
+ const a = (i / 60) * Math.PI * 2 - Math.PI / 2;
+ const major = i % 5 === 0;
+ const inner = r - (major ? 18 : 10);
+ actx.beginPath();
+ actx.moveTo(cx + Math.cos(a) * inner, cy + Math.sin(a) * inner);
+ actx.lineTo(cx + Math.cos(a) * (r - 4), cy + Math.sin(a) * (r - 4));
+ actx.strokeStyle = major ? '#e8ecf4' : '#3a4258';
+ actx.lineWidth = major ? 2.5 : 1;
+ actx.stroke();
+ }
+
+ // Numbers
+ actx.fillStyle = '#9ca3af';
+ actx.font = '600 22px system-ui, sans-serif';
+ actx.textAlign = 'center';
+ actx.textBaseline = 'middle';
+ for (let n = 1; n <= 12; n++) {
+ const a = (n / 12) * Math.PI * 2 - Math.PI / 2;
+ actx.fillText(String(n), cx + Math.cos(a) * (r - 36), cy + Math.sin(a) * (r - 36));
+ }
+
+ const h = now.getHours() % 12;
+ const m = now.getMinutes();
+ const s = now.getSeconds();
+ const ms = now.getMilliseconds();
+
+ const sAngle = ((s + ms / 1000) / 60) * Math.PI * 2 - Math.PI / 2;
+ const mAngle = ((m + s / 60) / 60) * Math.PI * 2 - Math.PI / 2;
+ const hAngle = ((h + m / 60) / 12) * Math.PI * 2 - Math.PI / 2;
+
+ function hand(angle, len, width, color) {
+ actx.beginPath();
+ actx.moveTo(cx - Math.cos(angle) * 12, cy - Math.sin(angle) * 12);
+ actx.lineTo(cx + Math.cos(angle) * len, cy + Math.sin(angle) * len);
+ actx.strokeStyle = color;
+ actx.lineWidth = width;
+ actx.lineCap = 'round';
+ actx.stroke();
+ }
+
+ hand(hAngle, r * 0.5, 7, '#e8ecf4');
+ hand(mAngle, r * 0.68, 5, '#93c5fd');
+ hand(sAngle, r * 0.78, 2, '#6ee7b7');
+
+ // Center cap
+ actx.beginPath();
+ actx.arc(cx, cy, 6, 0, Math.PI * 2);
+ actx.fillStyle = '#6ee7b7';
+ actx.fill();
+ }
+
+ /* ========== FLIP ========== */
+ const flipEl = document.getElementById('flip');
+ const flipDigits = [];
+
+ function buildFlip() {
+ flipEl.innerHTML = '';
+ const parts = ['h', 'h', ':', 'm', 'm', ':', 's', 's'];
+ let di = 0;
+ let group = null;
+ parts.forEach(function (p) {
+ if (p === ':') {
+ const c = document.createElement('div');
+ c.className = 'flip-colon';
+ c.textContent = ':';
+ flipEl.appendChild(c);
+ group = null;
+ return;
+ }
+ if (!group) {
+ group = document.createElement('div');
+ group.className = 'flip-group';
+ flipEl.appendChild(group);
+ }
+ const d = document.createElement('div');
+ d.className = 'flip-digit';
+ d.innerHTML =
+ '<div class="base">' +
+ '<div class="half top"><span>0</span></div>' +
+ '<div class="half bottom"><span>0</span></div>' +
+ '</div>';
+ group.appendChild(d);
+ flipDigits.push({ el: d, value: -1 });
+ di++;
+ });
+ }
+ buildFlip();
+
+ function setFlipDigit(idx, num) {
+ const d = flipDigits[idx];
+ if (d.value === num) return;
+ const prev = d.value < 0 ? num : d.value;
+ d.value = num;
+ const base = d.el.querySelector('.base');
+ const topSpan = base.querySelector('.top span');
+ const botSpan = base.querySelector('.bottom span');
+
+ // Remove old animation cards
+ d.el.querySelectorAll('.flip-card').forEach(function (c) { c.remove(); });
+
+ if (prev === num) {
+ topSpan.textContent = num;
+ botSpan.textContent = num;
+ return;
+ }
+
+ // Top flip card (shows old, flips away)
+ const topCard = document.createElement('div');
+ topCard.className = 'flip-card top-flip flipping-top';
+ topCard.innerHTML = '<span>' + prev + '</span>';
+
+ // Bottom flip card (shows new, flips in)
+ const botCard = document.createElement('div');
+ botCard.className = 'flip-card bottom-flip';
+ botCard.innerHTML = '<span>' + num + '</span>';
+ botCard.style.transform = 'rotateX(90deg)';
+
+ // Update base: top shows new immediately underneath, bottom keeps old until mid
+ topSpan.textContent = num;
+ botSpan.textContent = prev;
+
+ d.el.appendChild(topCard);
+ d.el.appendChild(botCard);
+
+ requestAnimationFrame(function () {
+ botCard.classList.add('flipping-bottom');
+ });
+
+ topCard.addEventListener('animationend', function () {
+ topCard.remove();
+ });
+ botCard.addEventListener('animationend', function () {
+ botSpan.textContent = num;
+ botCard.remove();
+ });
+ }
+
+ function updateFlip(now) {
+ const h = now.getHours();
+ const m = now.getMinutes();
+ const s = now.getSeconds();
+ const str =
+ String(h).padStart(2, '0') +
+ String(m).padStart(2, '0') +
+ String(s).padStart(2, '0');
+ for (let i = 0; i < 6; i++) {
+ setFlipDigit(i, str[i]);
+ }
+ }
+
+ /* ========== BINARY ========== */
+ const binEl = document.getElementById('binary');
+ // Columns: H1 H2 | M1 M2 | S1 S2 with bit heights 2,4,3,4,3,4
+ const binConfig = [
+ { label: 'H', bits: 2 },
+ { label: 'H', bits: 4 },
+ { label: 'M', bits: 3 },
+ { label: 'M', bits: 4 },
+ { label: 'S', bits: 3 },
+ { label: 'S', bits: 4 }
+ ];
+ const binCols = [];
+
+ function buildBinary() {
+ binEl.innerHTML = '';
+ binConfig.forEach(function (cfg) {
+ const col = document.createElement('div');
+ col.className = 'bin-col';
+ const lab = document.createElement('div');
+ lab.className = 'bin-label';
+ lab.textContent = cfg.label;
+ const bits = document.createElement('div');
+ bits.className = 'bin-bits';
+ const leds = [];
+ for (let i = 0; i < cfg.bits; i++) {
+ const led = document.createElement('div');
+ led.className = 'led';
+ bits.appendChild(led);
+ leds.push(led);
+ }
+ const val = document.createElement('div');
+ val.className = 'bin-value';
+ val.textContent = '0';
+ col.appendChild(lab);
+ col.appendChild(bits);
+ col.appendChild(val);
+ binEl.appendChild(col);
+ binCols.push({ leds: leds, val: val, bits: cfg.bits });
+ });
+ }
+ buildBinary();
+
+ function updateBinary(now) {
+ const h = now.getHours();
+ const m = now.getMinutes();
+ const s = now.getSeconds();
+ const digits = [
+ Math.floor(h / 10), h % 10,
+ Math.floor(m / 10), m % 10,
+ Math.floor(s / 10), s % 10
+ ];
+ digits.forEach(function (digit, i) {
+ const col = binCols[i];
+ col.val.textContent = digit;
+ for (let b = 0; b < col.bits; b++) {
+ if (digit & (1 << b)) col.leds[b].classList.add('on');
+ else col.leds[b].classList.remove('on');
+ }
+ });
+ }
+
+ /* ========== WORD ========== */
+ // 11x10 grid classic layout
+ const WORD_GRID = [
+ 'I','T','L','I','S','A','S','T','I','M','E',
+ 'A','C','Q','U','A','R','T','E','R','D','C',
+ 'T','W','E','N','T','Y','F','I','V','E','X',
+ 'H','A','L','F','B','T','E','N','F','T','O',
+ 'P','A','S','T','E','R','U','N','I','N','E',
+ 'O','N','E','S','I','X','T','H','R','E','E',
+ 'F','O','U','R','F','I','V','E','T','W','O',
+ 'E','I','G','H','T','E','L','E','V','E','N',
+ 'S','E','V','E','N','T','W','E','L','V','E',
+ 'T','E','N','S','E','O','C','L','O','C','K'
+ ];
+ // Phrases as index ranges [start, end) inclusive start, exclusive-style length via list of indices
+ const W = {
+ IT: [0,1], IS: [3,4], A: [11], QUARTER: [13,14,15,16,17,18,19],
+ TWENTY: [22,23,24,25,26,27], FIVE_M: [28,29,30,31],
+ HALF: [33,34,35,36], TEN_M: [38,39,40], TO: [42,43],
+ PAST: [44,45,46,47],
+ ONE: [55,56,57], TWO: [74,75,76], THREE: [61,62,63,64,65],
+ FOUR: [66,67,68,69], FIVE_H: [70,71,72,73], SIX: [58,59,60],
+ SEVEN: [88,89,90,91,92], EIGHT: [77,78,79,80,81],
+ NINE: [51,52,53,54], TEN_H: [99,100,101],
+ ELEVEN: [82,83,84,85,86,87], TWELVE: [93,94,95,96,97,98],
+ OCLOCK: [104,105,106,107,108,109]
+ };
+
+ const wordEl = document.getElementById('word');
+ const wordSpans = [];
+
+ function buildWord() {
+ wordEl.innerHTML = '';
+ WORD_GRID.forEach(function (ch) {
+ const s = document.createElement('span');
+ s.textContent = ch;
+ wordEl.appendChild(s);
+ wordSpans.push(s);
+ });
+ }
+ buildWord();
+
+ function light(indices) {
+ indices.forEach(function (i) { wordSpans[i].classList.add('lit'); });
+ }
+
+ function updateWord(now) {
+ wordSpans.forEach(function (s) { s.classList.remove('lit'); });
+ let h = now.getHours() % 12;
+ let m = now.getMinutes();
+ // round to nearest 5
+ let rm = Math.round(m / 5) * 5;
+ if (rm === 60) { rm = 0; h = (h + 1) % 12; }
+ // display hour: for "to" use next hour
+ let displayH = h;
+ if (rm > 30) displayH = (h + 1) % 12;
+ if (displayH === 0) displayH = 12;
+
+ light(W.IT);
+ light(W.IS);
+
+ if (rm === 0) {
+ light(W.OCLOCK);
+ } else if (rm === 5) {
+ light(W.FIVE_M); light(W.PAST);
+ } else if (rm === 10) {
+ light(W.TEN_M); light(W.PAST);
+ } else if (rm === 15) {
+ light(W.A); light(W.QUARTER); light(W.PAST);
+ } else if (rm === 20) {
+ light(W.TWENTY); light(W.PAST);
+ } else if (rm === 25) {
+ light(W.TWENTY); light(W.FIVE_M); light(W.PAST);
+ } else if (rm === 30) {
+ light(W.HALF); light(W.PAST);
+ } else if (rm === 35) {
+ light(W.TWENTY); light(W.FIVE_M); light(W.TO);
+ } else if (rm === 40) {
+ light(W.TWENTY); light(W.TO);
+ } else if (rm === 45) {
+ light(W.A); light(W.QUARTER); light(W.TO);
+ } else if (rm === 50) {
+ light(W.TEN_M); light(W.TO);
+ } else if (rm === 55) {
+ light(W.FIVE_M); light(W.TO);
+ }
+
+ const hours = {
+ 1: W.ONE, 2: W.TWO, 3: W.THREE, 4: W.FOUR, 5: W.FIVE_H,
+ 6: W.SIX, 7: W.SEVEN, 8: W.EIGHT, 9: W.NINE, 10: W.TEN_H,
+ 11: W.ELEVEN, 12: W.TWELVE
+ };
+ light(hours[displayH]);
+ }
+
+ /* ========== LOOP ========== */
+ let lastSec = -1;
+ function tick() {
+ const now = new Date();
+ drawAnalog(now);
+ const sec = now.getSeconds();
+ if (sec !== lastSec) {
+ lastSec = sec;
+ updateFlip(now);
+ updateBinary(now);
+ updateWord(now);
+ }
+ requestAnimationFrame(tick);
+ }
+ // initial
+ (function init() {
+ const now = new Date();
+ drawAnalog(now);
+ updateFlip(now);
+ updateBinary(now);
+ updateWord(now);
+ requestAnimationFrame(tick);
+ })();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/9ba7a5497a50/grok.png b/data/artifacts/9ba7a5497a50/grok.png
new file mode 100644
index 0000000..89d1fdb
Binary files /dev/null and b/data/artifacts/9ba7a5497a50/grok.png differ
diff --git a/data/artifacts/9ba7a5497a50/hermes3-8b.html b/data/artifacts/9ba7a5497a50/hermes3-8b.html
new file mode 100644
index 0000000..56ecc09
--- /dev/null
+++ b/data/artifacts/9ba7a5497a50/hermes3-8b.html
@@ -0,0 +1,185 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>4-in-1 Clock Gallery</title>
+ <style>
+ body {
+ background-color: #222;
+ color: white;
+ font-family: Arial, sans-serif;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ }
+
+ .clock-container {
+ display: flex;
+ gap: 10px;
+ }
+
+ canvas {
+ background-color: #333;
+ border-radius: 50%;
+ }
+ </style>
+</head>
+<body>
+<div class="clock-container">
+ <canvas id="analog" width="150" height="150"></canvas>
+ <canvas id="flip" width="200" height="150"></canvas>
+ <canvas id="binary" width="300" height="200"></canvas>
+ <canvas id="word" width="250" height="100"></canvas>
+</div>
+
+<script>
+ const analogCtx = document.getElementById("analog").getContext("2d");
+ const flipCtx = document.getElementById("flip").getContext("2d");
+ const binaryCtx = document.getElementById("binary").getContext("2d");
+ const wordCtx = document.getElementById("word").getContext("2d");
+
+ function drawAnalog() {
+ analogCtx.clearRect(0, 0, 150, 150);
+ const now = new Date();
+ const hours = now.getHours();
+ const minutes = now.getMinutes();
+ const seconds = now.getSeconds();
+
+ analogCtx.translate(75, 75);
+ analogCtx.scale(-0.5, 0.5);
+
+ for (let i = 0; i < 12; i++) {
+ let angle = Math.PI / 6 * i;
+ analogCtx.beginPath();
+ analogCtx.arc(0, 0, 90, angle, angle + Math.PI / 6);
+ if (i % 3 === 0) {
+ analogCtx.fillStyle = "#fff";
+ analogCtx.fill();
+ } else {
+ analogCtx.strokeStyle = "#ccc";
+ analogCtx.stroke();
+ }
+ }
+
+ let sAngle = Math.PI / 6 * (seconds % 12);
+ let eAngle = sAngle + Math.PI / 6;
+ analogCtx.beginPath();
+ analogCtx.arc(0, 0, 80, sAngle, eAngle);
+ analogCtx.strokeStyle = "#fff";
+ analogCtx.lineWidth = 3;
+ analogCtx.stroke();
+
+ let mAngle = Math.PI / 6 * (minutes % 12 + Number(seconds >= 30));
+ let emAngle = mAngle + Math.PI / 3;
+ analogCtx.beginPath();
+ analogCtx.arc(0, 0, 70, mAngle, emAngle);
+ analogCtx.strokeStyle = "#fff";
+ analogCtx.lineWidth = 4;
+ analogCtx.stroke();
+
+ analogCtx.fillText(hours + ":" + (minutes < 10 ? "0" : "") + minutes, -60, -40);
+ }
+
+ function drawFlip() {
+ flipCtx.clearRect(0, 0, 200, 150);
+
+ const now = new Date();
+ let hours = now.getHours();
+ let minutes = now.getMinutes();
+
+ flipCtx.fillStyle = "#fff";
+ for (let i = 1; i < 4; i++) {
+ for (let j = 1; j < 7; j++) {
+ if ((i === Math.floor(hours / 10) && j === hours % 10) || (i === Math.floor(minutes / 10) && j === minutes % 10)) {
+ flipCtx.fillRect((j - 1) * 25, i * 20 - 5, 24, 15);
+ } else {
+ flipCtx.strokeText(".", (j - 1) * 25 + 8, i * 20 + 13);
+ }
+ }
+ }
+ }
+
+ function drawBinary() {
+ binaryCtx.clearRect(0, 0, 300, 200);
+
+ const now = new Date();
+ let seconds = now.getSeconds();
+
+ for (let i = 1; i < 6; i++) {
+ for (let j = 1; j < 7; j++) {
+ if ((i === Math.floor(seconds / 10) && j === seconds % 10)) {
+ binaryCtx.fillStyle = "#fff";
+ } else {
+ binaryCtx.fillStyle = "#ccc";
+ }
+ for (let k = 0; k < 5; k++) {
+ if (binaryCtx.fillStyle === "#fff") {
+ if ((j - 1) * 50 + 10 * k <= Math.floor(seconds / 2)) {
+ binaryCtx.fillRect((j - 1) * 50, i * 25 - 5, 49, 12);
+ }
+ } else {
+ binaryCtx.strokeText(".", (j - 1) * 50 + 8, i * 25 + 7 + k);
+ }
+ }
+ }
+ }
+ }
+
+ function drawWord() {
+ wordCtx.clearRect(0, 0, 250, 100);
+
+ const now = new Date();
+ let hours = now.getHours();
+
+ wordCtx.textAlign = "center";
+ wordCtx.fillStyle = "#fff";
+ for (let i = 0; i < 6; i++) {
+ let word = "";
+ switch (i) {
+ case 0:
+ word = "T W E N";
+ break;
+ case 1:
+ word = "T W E N T H R O U G H";
+ break;
+ case 2:
+ word = "O N E A N D";
+ break;
+ case 3:
+ word = "T W E N F I V E";
+ break;
+ case 4:
+ word = "P A S T";
+ break;
+ }
+ if (hours >= i * 10 && hours < (i + 1) * 10) {
+ wordCtx.fillStyle = "#f00";
+ for (let j = 0; j < word.length; j++) {
+ wordCtx.fillRect(125 - 12 * word.length / 2, (i - 1.5) * 15, 12, 3);
+ wordCtx.strokeText(word[j], 125, (i - 1) * 15 + 7.5);
+ }
+ } else {
+ wordCtx.fillStyle = "#ccc";
+ for (let j = 0; j < word.length; j++) {
+ wordCtx.fillRect(125 - 12 * word.length / 2, (i - 1.5) * 15, 12, 3);
+ wordCtx.strokeText(word[j], 125, (i - 1) * 15 + 7.5);
+ }
+ }
+ }
+ }
+
+ function updateClocks() {
+ drawAnalog();
+ drawFlip();
+ drawBinary();
+ drawWord();
+ setTimeout(updateClocks, 1000);
+ }
+
+ updateClocks();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/9ba7a5497a50/hermes3-8b.png b/data/artifacts/9ba7a5497a50/hermes3-8b.png
new file mode 100644
index 0000000..ff11bcc
Binary files /dev/null and b/data/artifacts/9ba7a5497a50/hermes3-8b.png differ
diff --git a/data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.html b/data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..04dac9c
--- /dev/null
+++ b/data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.html
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Clock Gallery</title>
+<style>
+body { display: flex; justify-content: space-around; align-items: center; height: 100vh; background-color: #121212; color: #e0e0e0; font-family: 'Arial', sans-serif; }
+.clock { width: 20%; text-align: center; }
+.analog-clock, .flip-clock, .binary-clock, .word-clock { margin-bottom: 20px; }
+.canvas { position: relative; width: 100px; height: 100px; margin: auto; }
+.flip-digit { display: inline-block; font-size: 3em; line-height: 1em; width: 0.5em; text-align: center; vertical-align: top; position: relative; }
+.flip-digit::before, .flip-digit::after { content: attr(data-value); position: absolute; top: 0; left: 0; width: 100%; height: 50%; background-color: #424242; color: #e0e0e0; border-radius: 3px; overflow: hidden; }
+.flip-digit::after { top: 50%; transform-origin: bottom; }
+.flip-digit.flipping::before { transform: rotateX(-90deg); transition: transform 0.5s; }
+.flip-digit.flipping::after { transform: rotateX(0deg); transition: transform 0.5s; }
+.binary-clock span { display: inline-block; width: 1em; font-size: 2em; margin-right: 0.2em; }
+</style>
+</head>
+<body>
+<div class="clock">
+ <div class="analog-clock">Analog Clock
+ <canvas class="canvas" id="analog"></canvas>
+ </div>
+ <div class="flip-clock">Flip Clock
+ <div id="flip">
+ <span class="flip-digit" data-value="0">0</span><span class="flip-digit" data-value="0">0</span>:<span class="flip-digit" data-value="0">0</span><span class="flip-digit" data-value="0">0</span>
+ </div>
+ </div>
+ <div class="binary-clock">Binary Clock
+ <div id="binary"></div>
+ </div>
+ <div class="word-clock">Word Clock
+ <div id="word"></div>
+ </div>
+<script>
+function updateClocks() {
+ const now = new Date();
+ let hours = now.getHours(), minutes = now.getMinutes(), seconds = now.getSeconds();
+
+ // Analog Clock
+ const canvas = document.getElementById('analog');
+ const ctx = canvas.getContext('2d');
+ const radius = canvas.height / 2;
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ drawFace(ctx, radius);
+ drawNumbers(ctx, radius);
+ drawTime(ctx, radius, hours % 12, minutes, seconds);
+
+ // Flip Clock
+ function flipDigit(el, value) {
+ if (el.dataset.value !== value.toString()) {
+ el.classList.add('flipping');
+ setTimeout(() => {
+ el.dataset.value = value;
+ el.textContent = value;
+ el.classList.remove('flipping');
+ }, 500);
+ }
+ }
+ document.querySelectorAll('#flip .flip-digit').forEach((digit, i) => {
+ flipDigit(digit, [Math.floor(hours / 10), hours % 10, Math.floor(minutes / 10), minutes % 10][i]);
+ });
+
+ // Binary Clock
+ const binary = document.getElementById('binary');
+ binary.innerHTML = [
+ (hours >> 4) & 1, (hours >> 3) & 1, (hours >> 2) & 1, (hours >> 1) & 1, hours & 1,
+ (minutes >> 4) & 1, (minutes >> 3) & 1, (minutes >> 2) & 1, (minutes >> 1) & 1, minutes & 1
+ ].map(bit => `<span>${bit}</span>`).join('');
+
+ // Word Clock
+ const words = ["OCLOCK", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "TEN", "ELEVEN"];
+ const wordTime = `${hours > 12 ? hours - 12 : hours}:${minutes}`;
+ let wordClockText;
+ if (minutes === 0) {
+ wordClockText = `IT IS ${words[hours % 12]}`;
+ } else if (minutes === 30) {
+ wordClockText = `HALF PAST ${words[(hours - 1) % 12]}`;
+ } else if (minutes < 30) {
+ const minsWord = minutes === 15 ? "QUARTER" : words[minutes];
+ wordClockText = `${minsWord} PAST ${words[hours % 12]}`;
+ } else {
+ const minsWord = minutes === 45 ? "QUARTER" : words[60 - minutes];
+ wordClockText = `${minsWord} TO ${words[hours % 12 + 1]}`;
+ }
+ document.getElementById('word').textContent = wordClockText;
+}
+
+function drawFace(ctx, radius) {
+ ctx.beginPath();
+ ctx.arc(0, 0, radius, 0, 2 * Math.PI);
+ ctx.fillStyle = '#424242';
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(0, 0, radius * 0.1, 0, 2 * Math.PI);
+ ctx.fillStyle = '#e0e0e0';
+ ctx.fill();
+}
+
+function drawNumbers(ctx, radius) {
+ ctx.font = radius * 0.15 + "px arial";
+ ctx.textBaseline = "middle";
+ ctx.textAlign = "center";
+
+ for(let num = 1; num < 13; num++){
+ const ang = num * Math.PI / 6;
+ ctx.rotate(ang);
+ ctx.translate(0, -radius * 0.85);
+ ctx.rotate(-ang);
+ ctx.fillText(num.toString(), 0, 0);
+ ctx.rotate(ang);
+ ctx.translate(0, radius * 0.85);
+ ctx.rotate(-ang);
+ }
+}
+
+function drawTime(ctx, radius, hour, minute, second){
+ hour = hour % 12;
+ hour = (hour * Math.PI / 6) + (minute * Math.PI / (6 * 60)) + (second * Math.PI / (360 * 60));
+ drawHand(ctx, hour, radius*0.5, radius*0.07);
+
+ minute = (minute * Math.PI / 30) + (second * Math.PI / (30 * 60));
+ drawHand(ctx, minute, radius*0.8, radius*0.07);
+
+ second = (second * Math.PI / 30);
+ drawHand(ctx, second, radius*0.9, radius*0.02);
+}
+
+function drawHand(ctx, pos, length, width) {
+ ctx.beginPath();
+ ctx.lineWidth = width;
+ ctx.lineCap = "round";
+ ctx.moveTo(0,0);
+ ctx.rotate(pos);
+ ctx.lineTo(0, -length);
+ ctx.strokeStyle = '#e0e0e0';
+ ctx.stroke();
+ ctx.rotate(-pos);
+}
+
+setInterval(updateClocks, 1000);
+updateClocks(); // Initial call
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.png b/data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..0e4743c
Binary files /dev/null and b/data/artifacts/9ba7a5497a50/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/9ba7a5497a50/kimi.html b/data/artifacts/9ba7a5497a50/kimi.html
new file mode 100644
index 0000000..63f4917
--- /dev/null
+++ b/data/artifacts/9ba7a5497a50/kimi.html
@@ -0,0 +1,493 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Chrono Gallery</title>
+<style>
+* { box-sizing: border-box; margin: 0; padding: 0; }
+body {
+ background: #0a0a0a;
+ color: #e0e0e0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 40px 20px;
+}
+.gallery {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 40px;
+ width: 100%;
+ max-width: 1400px;
+}
+.clock-card {
+ background: #151515;
+ border-radius: 20px;
+ padding: 40px 30px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ box-shadow: 0 10px 40px rgba(0,0,0,0.6);
+ border: 1px solid #252525;
+ position: relative;
+ overflow: hidden;
+}
+.clock-card::before {
+ content: '';
+ position: absolute;
+ top: 0; left: 0; right: 0; height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
+}
+h2 {
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 4px;
+ color: #555;
+ margin-bottom: 30px;
+ font-weight: 700;
+}
+
+/* Analog Clock */
+.analog-face {
+ width: 220px;
+ height: 220px;
+ position: relative;
+ border-radius: 50%;
+ background: radial-gradient(circle at 30% 30%, #2a2a2a 0%, #111 100%);
+ border: 2px solid #333;
+ box-shadow: inset 0 0 30px rgba(0,0,0,0.8), 0 5px 15px rgba(0,0,0,0.5);
+}
+.hand {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-origin: bottom center;
+ border-radius: 2px;
+ box-shadow: 0 0 5px rgba(0,0,0,0.3);
+}
+.hour-hand {
+ width: 6px;
+ height: 60px;
+ margin-left: -3px;
+ margin-top: -60px;
+ background: #e0e0e0;
+ z-index: 3;
+}
+.minute-hand {
+ width: 4px;
+ height: 85px;
+ margin-left: -2px;
+ margin-top: -85px;
+ background: #aaa;
+ z-index: 2;
+}
+.second-hand {
+ width: 2px;
+ height: 95px;
+ margin-left: -1px;
+ margin-top: -95px;
+ background: #ff3333;
+ z-index: 4;
+ transform-origin: 50% 85%;
+}
+.center-dot {
+ position: absolute;
+ width: 14px;
+ height: 14px;
+ background: #ff3333;
+ border-radius: 50%;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 5;
+ border: 3px solid #111;
+}
+.tick {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 2px;
+ height: 12px;
+ background: #444;
+ margin-top: -6px;
+ margin-left: -1px;
+ transform-origin: center;
+}
+
+/* Flip/Roller Clock */
+.flip-display {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ font-family: 'Courier New', Courier, monospace;
+ font-weight: bold;
+}
+.digit-group {
+ display: flex;
+ gap: 6px;
+}
+.digit-window {
+ width: 70px;
+ height: 90px;
+ background: #000;
+ border-radius: 8px;
+ overflow: hidden;
+ position: relative;
+ border: 2px solid #333;
+ box-shadow: inset 0 0 20px rgba(0,0,0,0.9), 0 2px 5px rgba(0,0,0,0.5);
+}
+.digit-strip {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ transition: transform 0.6s cubic-bezier(0.4, 0.0, 0.2, 1);
+}
+.digit-strip div {
+ height: 90px;
+ line-height: 90px;
+ font-size: 56px;
+ color: #f0f0f0;
+ text-align: center;
+ font-weight: 700;
+ text-shadow: 0 0 5px rgba(255,255,255,0.2);
+ background: linear-gradient(180deg, #1a1a1a 0%, #0d0d0d 50%, #000 100%);
+}
+.separator {
+ font-size: 40px;
+ color: #333;
+ line-height: 90px;
+ font-weight: 300;
+ animation: pulse 2s infinite;
+}
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.4; }
+}
+
+/* Binary Clock */
+.binary-container {
+ display: flex;
+ gap: 15px;
+ align-items: flex-end;
+ height: 200px;
+ padding: 10px;
+}
+.binary-col {
+ display: flex;
+ flex-direction: column-reverse;
+ gap: 12px;
+ align-items: center;
+}
+.bit {
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ background: #1a1a1a;
+ border: 2px solid #2a2a2a;
+ transition: all 0.4s cubic-bezier(0.4, 0.0, 0.2, 1);
+ box-shadow: inset 0 2px 5px rgba(0,0,0,0.5);
+}
+.bit.active {
+ background: #00ff9d;
+ border-color: #00ff9d;
+ box-shadow: 0 0 20px rgba(0, 255, 157, 0.5), inset 0 0 10px rgba(255,255,255,0.5);
+}
+.binary-label {
+ font-size: 0.8rem;
+ color: #444;
+ margin-top: 10px;
+ font-family: monospace;
+ font-weight: bold;
+}
+.binary-value {
+ font-size: 0.7rem;
+ color: #333;
+ margin-bottom: 5px;
+ font-family: monospace;
+}
+
+/* Word Clock */
+.word-grid {
+ width: 300px;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 8px;
+ font-family: 'Georgia', serif;
+ text-transform: uppercase;
+ line-height: 1.6;
+ padding: 10px;
+}
+.word {
+ padding: 6px 10px;
+ color: #2a2a2a;
+ transition: all 0.4s ease;
+ font-size: 1.25rem;
+ letter-spacing: 2px;
+ font-weight: 400;
+ border-radius: 4px;
+}
+.word.active {
+ color: #fff;
+ text-shadow: 0 0 15px rgba(255,255,255,0.8);
+ font-weight: 700;
+ background: rgba(255,255,255,0.05);
+}
+</style>
+</head>
+<body>
+ <div class="gallery">
+ <!-- Analog Clock -->
+ <div class="clock-card">
+ <h2>Analog</h2>
+ <div class="analog-face" id="analog-face">
+ <div class="hand hour-hand" id="h-hand"></div>
+ <div class="hand minute-hand" id="m-hand"></div>
+ <div class="hand second-hand" id="s-hand"></div>
+ <div class="center-dot"></div>
+ </div>
+ </div>
+
+ <!-- Flip Clock -->
+ <div class="clock-card">
+ <h2>Flip</h2>
+ <div class="flip-display">
+ <div class="digit-group">
+ <div class="digit-window"><div class="digit-strip" id="h1"></div></div>
+ <div class="digit-window"><div class="digit-strip" id="h2"></div></div>
+ </div>
+ <div class="separator">:</div>
+ <div class="digit-group">
+ <div class="digit-window"><div class="digit-strip" id="m1"></div></div>
+ <div class="digit-window"><div class="digit-strip" id="m2"></div></div>
+ </div>
+ <div class="separator">:</div>
+ <div class="digit-group">
+ <div class="digit-window"><div class="digit-strip" id="s1"></div></div>
+ <div class="digit-window"><div class="digit-strip" id="s2"></div></div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Binary Clock -->
+ <div class="clock-card">
+ <h2>Binary</h2>
+ <div class="binary-container" id="binary-container"></div>
+ </div>
+
+ <!-- Word Clock -->
+ <div class="clock-card">
+ <h2>Word</h2>
+ <div class="word-grid" id="word-grid"></div>
+ </div>
+ </div>
+
+<script>
+// Initialize Analog Ticks
+const analogFace = document.getElementById('analog-face');
+for (let i = 0; i < 12; i++) {
+ const tick = document.createElement('div');
+ tick.className = 'tick';
+ const angle = i * 30;
+ tick.style.transform = `translateY(-95px) rotate(${angle}deg)`;
+ if (i % 3 === 0) {
+ tick.style.height = '16px';
+ tick.style.marginTop = '-8px';
+ tick.style.background = '#666';
+ tick.style.width = '4px';
+ tick.style.marginLeft = '-2px';
+ }
+ analogFace.appendChild(tick);
+}
+
+// Initialize Flip Clock Strips
+function initStrip(id) {
+ const strip = document.getElementById(id);
+ for (let i = 0; i <= 9; i++) {
+ const div = document.createElement('div');
+ div.textContent = i;
+ strip.appendChild(div);
+ }
+}
+['h1', 'h2', 'm1', 'm2', 's1', 's2'].forEach(initStrip);
+
+// Initialize Binary Clock (BCD - 6 columns: HH MM SS)
+const binaryContainer = document.getElementById('binary-container');
+const binaryLabels = ['H', 'h', 'M', 'm', 'S', 's'];
+const binaryValues = [2, 9, 5, 9, 5, 9]; // Max values for each digit
+
+binaryLabels.forEach((label, idx) => {
+ const col = document.createElement('div');
+ col.className = 'binary-col';
+
+ const valDisplay = document.createElement('div');
+ valDisplay.className = 'binary-value';
+ valDisplay.textContent = '0';
+ col.appendChild(valDisplay);
+
+ for (let bit = 0; bit < 4; bit++) {
+ const dot = document.createElement('div');
+ dot.className = 'bit';
+ dot.dataset.col = idx;
+ dot.dataset.bit = bit; // 0=1, 1=2, 2=4, 3=8 (bottom to top)
+ col.appendChild(dot);
+ }
+
+ const lbl = document.createElement('div');
+ lbl.className = 'binary-label';
+ lbl.textContent = label;
+ col.appendChild(lbl);
+
+ binaryContainer.appendChild(col);
+});
+
+// Initialize Word Clock
+const wordGrid = document.getElementById('word-grid');
+const wordsLayout = [
+ ['IT', 'IS', 'HALF', 'TEN'],
+ ['QUARTER', 'TWENTY'],
+ ['FIVE', 'MINUTES'],
+ ['PAST', 'TO', 'ONE'],
+ ['TWO', 'THREE'],
+ ['FOUR', 'FIVEx'], // x denotes hour version
+ ['SIX', 'SEVEN'],
+ ['EIGHT', 'NINE'],
+ ['TENx', 'ELEVEN'],
+ ['TWELVE', 'OCLOCK']
+];
+
+const wordElements = {};
+wordsLayout.forEach(row => {
+ row.forEach(word => {
+ const span = document.createElement('span');
+ span.className = 'word';
+ span.textContent = word.replace('x', '');
+ span.dataset.word = word;
+ wordGrid.appendChild(span);
+ wordElements[word] = span;
+ });
+});
+
+function updateClocks() {
+ const now = new Date();
+ const h = now.getHours();
+ const m = now.getMinutes();
+ const s = now.getSeconds();
+ const ms = now.getMilliseconds();
+
+ // Analog
+ const hDeg = (h % 12) * 30 + m * 0.5;
+ const mDeg = m * 6 + s * 0.1;
+ const sDeg = s * 6 + ms * 0.006;
+ document.getElementById('h-hand').style.transform = `rotate(${hDeg}deg)`;
+ document.getElementById('m-hand').style.transform = `rotate(${mDeg}deg)`;
+ document.getElementById('s-hand').style.transform = `rotate(${sDeg}deg)`;
+
+ // Flip Clock
+ const hs = String(h).padStart(2, '0');
+ const ms_str = String(m).padStart(2, '0');
+ const ss = String(s).padStart(2, '0');
+
+ function setDigit(id, val) {
+ document.getElementById(id).style.transform = `translateY(-${val * 90}px)`;
+ }
+ setDigit('h1', parseInt(hs[0]));
+ setDigit('h2', parseInt(hs[1]));
+ setDigit('m1', parseInt(ms_str[0]));
+ setDigit('m2', parseInt(ms_str[1]));
+ setDigit('s1', parseInt(ss[0]));
+ setDigit('s2', parseInt(ss[1]));
+
+ // Binary Clock (BCD)
+ const digits = [
+ Math.floor(h / 10), h % 10,
+ Math.floor(m / 10), m % 10,
+ Math.floor(s / 10), s % 10
+ ];
+
+ digits.forEach((digit, colIdx) => {
+ // Update value display
+ const col = binaryContainer.children[colIdx];
+ col.querySelector('.binary-value').textContent = digit;
+
+ // Update bits (bottom to top: 1, 2, 4, 8)
+ for (let bitIdx = 0; bitIdx < 4; bitIdx++) {
+ const weight = Math.pow(2, bitIdx);
+ const bitEl = col.querySelector(`.bit[data-bit="${bitIdx}"]`);
+ if (digit & weight) {
+ bitEl.classList.add('active');
+ } else {
+ bitEl.classList.remove('active');
+ }
+ }
+ });
+
+ // Word Clock
+ // Reset
+ Object.values(wordElements).forEach(el => el.classList.remove('active'));
+
+ // Always IT IS
+ wordElements['IT'].classList.add('active');
+ wordElements['IS'].classList.add('active');
+
+ // Round to nearest 5 minutes
+ const totalM = h * 60 + m;
+ const rounded = Math.round(totalM / 5) * 5;
+ const displayH = ((Math.floor(rounded / 60) - 1) % 12) + 1;
+ const displayM = rounded % 60;
+
+ const hourMap = {
+ 1: 'ONE', 2: 'TWO', 3: 'THREE', 4: 'FOUR', 5: 'FIVEx',
+ 6: 'SIX', 7: 'SEVEN', 8: 'EIGHT', 9: 'NINE', 10: 'TENx',
+ 11: 'ELEVEN', 12: 'TWELVE'
+ };
+
+ if (displayM === 0) {
+ wordElements[hourMap[displayH]].classList.add('active');
+ wordElements['OCLOCK'].classList.add('active');
+ } else if (displayM === 15) {
+ wordElements['QUARTER'].classList.add('active');
+ wordElements['PAST'].classList.add('active');
+ wordElements[hourMap[displayH]].classList.add('active');
+ } else if (displayM === 30) {
+ wordElements['HALF'].classList.add('active');
+ wordElements['PAST'].classList.add('active');
+ wordElements[hourMap[displayH]].classList.add('active');
+ } else if (displayM === 45) {
+ wordElements['QUARTER'].classList.add('active');
+ wordElements['TO'].classList.add('active');
+ wordElements[hourMap[(displayH % 12) + 1]].classList.add('active');
+ } else if (displayM < 30) {
+ if (displayM === 5) wordElements['FIVE'].classList.add('active');
+ if (displayM === 10) wordElements['TEN'].classList.add('active');
+ if (displayM === 20) wordElements['TWENTY'].classList.add('active');
+ if (displayM === 25) {
+ wordElements['TWENTY'].classList.add('active');
+ wordElements['FIVE'].classList.add('active');
+ }
+ wordElements['PAST'].classList.add('active');
+ wordElements[hourMap[displayH]].classList.add('active');
+ } else {
+ const toVal = 60 - displayM;
+ if (toVal === 5) wordElements['FIVE'].classList.add('active');
+ if (toVal === 10) wordElements['TEN'].classList.add('active');
+ if (toVal === 20) wordElements['TWENTY'].classList.add('active');
+ if (toVal === 25) {
+ wordElements['TWENTY'].classList.add('active');
+ wordElements['FIVE'].classList.add('active');
+ }
+ wordElements['TO'].classList.add('active');
+ wordElements[hourMap[(displayH % 12) + 1]].classList.add('active');
+ }
+
+ requestAnimationFrame(updateClocks);
+}
+
+updateClocks();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/9ba7a5497a50/kimi.png b/data/artifacts/9ba7a5497a50/kimi.png
new file mode 100644
index 0000000..469c901
Binary files /dev/null and b/data/artifacts/9ba7a5497a50/kimi.png differ
diff --git a/data/artifacts/9ba7a5497a50/qwen25-7b.html b/data/artifacts/9ba7a5497a50/qwen25-7b.html
new file mode 100644
index 0000000..238de90
--- /dev/null
+++ b/data/artifacts/9ba7a5497a50/qwen25-7b.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Real-Time Clocks</title>
+ <style>
+ body {
+ background: #222;
+ color: white;
+ font-family: sans-serif;
+ text-align: center;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ height: 100vh;
+ }
+ .clock {
+ width: 25%;
+ box-sizing: border-box;
+ }
+ </style>
+</head>
+<body>
+ <div class="clock">
+ <canvas id="analogClock" width="150" height="150"></canvas>
+ <script>
+ function drawAnalogClock(ctx, date) {
+ const radius = 75;
+ ctx.clearRect(0, 0, 150, 150);
+ ctx.beginPath();
+ ctx.arc(75, 75, radius, 0, Math.PI * 2);
+ ctx.fillStyle = "#fff";
+ ctx.fill();
+ ctx.strokeStyle = "#0ff";
+ ctx.lineWidth = 3;
+ ctx.stroke();
+ const secondHandLength = radius - 18;
+ const minuteHandLength = radius - 45;
+ const hourHandLength = radius - 20;
+ const seconds = date.getSeconds();
+ const minutes = date.getMinutes();
+ const hours = (date.getHours() % 12) + (minutes / 60);
+ ctx.save();
+ ctx.translate(75, 75);
+ ctx.rotate(date.setSeconds(seconds).getMinutes() * Math.PI / 30 - Math.PI / 2);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, -secondHandLength);
+ ctx.strokeStyle = "#f0f";
+ ctx.stroke();
+ ctx.restore();
+ ctx.save();
+ ctx.translate(75, 75);
+ ctx.rotate(date.getMinutes() * Math.PI / 30 - Math.PI / 2);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, -minuteHandLength);
+ ctx.strokeStyle = "#0ff";
+ ctx.stroke();
+ ctx.restore();
+ ctx.save();
+ ctx.translate(75, 75);
+ ctx.rotate((hours + minutes / 60) * Math.PI / 6 - Math.PI / 2);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, -hourHandLength);
+ ctx.strokeStyle = "#ff0";
+ ctx.stroke();
+ }
+ setInterval(() => {
+ const date = new Date();
+ drawAnalogClock(document.getElementById('analogClock').getContext('2d'), date);
+ }, 1000);
+ </script>
+ </div>
+ <div class="clock">
+ <canvas id="flipClock" width="150" height="150"></canvas>
+ <script>
+ function drawFlipClock(ctx, date) {
+ const size = 75;
+ ctx.clearRect(0, 0, 150, 150);
+ ctx.save();
+ ctx.translate(size / 2, size / 2);
+ ctx.rotate(date.getSeconds() * Math.PI / 30 - Math.PI / 2);
+ for (let i = 0; i < 60; i++) {
+ if (i % 5 == 0) ctx.fillStyle = "#fff";
+ else ctx.fillStyle = "#eee";
+ ctx.fillRect(i / 60 * size, -size / 2, 1, size);
+ }
+ ctx.restore();
+ }
+ setInterval(() => {
+ const date = new Date();
+ drawFlipClock(document.getElementById('flipClock').getContext('2d'), date);
+ }, 1000);
+ </script>
+ </div>
+ <div class="clock">
+ <canvas id="binaryClock" width="150" height="150"></canvas>
+ <script>
+ function drawBinaryClock(ctx, date) {
+ const size = 75;
+ ctx.clearRect(0, 0, 150, 150);
+ ctx.font = "32px sans-serif";
+ ctx.fillStyle = "#fff";
+ ctx.textAlign = "center";
+ ctx.fillText(date.getHours().toString(2).padStart(4, '0'), size / 2, 64);
+ ctx.fillText(date.getMinutes().toString(2).padStart(5, '0'), size / 2, 128);
+ ctx.fillText(date.getSeconds().toString(2).padStart(6, '0'), size / 2, 192);
+ }
+ setInterval(() => {
+ const date = new Date();
+ drawBinaryClock(document.getElementById('binaryClock').getContext('2d'), date);
+ }, 1000);
+ </script>
+ </div>
+ <div class="clock">
+ <p id="wordClock">IT IS HALF PAST TEN</p>
+ <script>
+ function updateWordClock() {
+ const now = new Date();
+ const hours = (now.getHours() % 12) + " o'clock";
+ const minutes = now.getMinutes().toString()[0] + " past " + (now.getHours() % 12);
+ document.getElementById('wordClock').innerText = `IT IS ${minutes} ${hours}`;
+ }
+ setInterval(updateWordClock, 60000);
+ </script>
+ </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/9ba7a5497a50/qwen25-7b.png b/data/artifacts/9ba7a5497a50/qwen25-7b.png
new file mode 100644
index 0000000..c8172cf
Binary files /dev/null and b/data/artifacts/9ba7a5497a50/qwen25-7b.png differ
diff --git a/data/artifacts/9ba7a5497a50/qwen3-14b.html b/data/artifacts/9ba7a5497a50/qwen3-14b.html
new file mode 100644
index 0000000..64fd2cd
--- /dev/null
+++ b/data/artifacts/9ba7a5497a50/qwen3-14b.html
@@ -0,0 +1,251 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Live Clock Gallery</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ background-color: #121212;
+ color: #ffffff;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ }
+ .clock-gallery {
+ display: flex;
+ gap: 20px;
+ width: 100%;
+ max-width: 1200px;
+ }
+ .clock-container {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ }
+ canvas {
+ background-color: #1e1e1e;
+ border: 2px solid #444;
+ border-radius: 8px;
+ }
+ .flip-clock, .binary-clock, .word-clock {
+ font-size: 24px;
+ text-align: center;
+ margin-top: 10px;
+ }
+ .word-clock {
+ font-size: 28px;
+ font-weight: bold;
+ }
+ </style>
+</head>
+<body>
+ <div class="clock-gallery">
+ <div class="clock-container">
+ <canvas id="analog-clock" width="200" height="200"></canvas>
+ <div class="flip-clock" id="flip-clock">--:--</div>
+ </div>
+ <div class="clock-container">
+ <canvas id="flip-clock-canvas" width="200" height="200"></canvas>
+ <div class="binary-clock" id="binary-clock">--:--</div>
+ </div>
+ <div class="clock-container">
+ <canvas id="binary-clock-canvas" width="200" height="200"></canvas>
+ <div class="word-clock" id="word-clock">IT IS HALF PAST TEN</div>
+ </div>
+ <div class="clock-container">
+ <canvas id="word-clock-canvas" width="200" height="200"></canvas>
+ <div class="flip-clock" id="flip-clock-2">--:--</div>
+ </div>
+ </div>
+
+ <script>
+ function drawAnalogClock(ctx) {
+ const now = new Date();
+ const seconds = now.getSeconds() + now.getMilliseconds() / 1000;
+ const minutes = now.getMinutes() + seconds / 60;
+ const hours = now.getHours() % 12 + minutes / 60;
+
+ ctx.clearRect(0, 0, 200, 200);
+ ctx.save();
+ ctx.translate(100, 100);
+ ctx.rotate(-Math.PI / 2);
+
+ // Draw clock face
+ ctx.beginPath();
+ ctx.arc(0, 0, 90, 0, 2 * Math.PI);
+ ctx.fillStyle = '#2a2a2a';
+ ctx.fill();
+ ctx.strokeStyle = '#444';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+
+ // Draw numbers
+ ctx.fillStyle = '#fff';
+ ctx.font = '16px sans-serif';
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+
+ for (let i = 1; i <= 12; i++) {
+ ctx.save();
+ ctx.rotate((i - 3) * Math.PI / 6);
+ ctx.fillText(i, 0, -45);
+ ctx.restore();
+ }
+
+ // Draw hour hand
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, -40);
+ ctx.strokeStyle = '#fff';
+ ctx.lineWidth = 6;
+ ctx.stroke();
+
+ // Draw minute hand
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, -70);
+ ctx.strokeStyle = '#fff';
+ ctx.lineWidth = 4;
+ ctx.stroke();
+
+ // Draw second hand
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(0, -90);
+ ctx.strokeStyle = '#f00';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+
+ ctx.restore();
+ }
+
+ function drawFlipClock(ctx) {
+ const now = new Date();
+ const time = now.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', hour12: false});
+ const [h, m] = time.split(':').map(Number);
+ const digits = [Math.floor(h / 10), h % 10, Math.floor(m / 10), m % 10];
+ const digitWidth = 40;
+ const spacing = 8;
+ const startX = 50;
+ const startY = 50;
+
+ ctx.clearRect(0, 0, 200, 200);
+ ctx.fillStyle = '#fff';
+ ctx.font = '24px monospace';
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'top';
+
+ for (let i = 0; i < 4; i++) {
+ const x = startX + i * (digitWidth + spacing);
+ const y = startY;
+ ctx.fillText(digits[i], x, y);
+ }
+ }
+
+ function drawBinaryClock(ctx) {
+ const now = new Date();
+ const h = now.getHours();
+ const m = now.getMinutes();
+ const s = now.getSeconds();
+ const binaryH = h.toString(2).padStart(5, '0');
+ const binaryM = m.toString(2).padStart(6, '0');
+ const binaryS = s.toString(2).padStart(6, '0');
+
+ const rows = [
+ binaryH,
+ binaryM,
+ binaryS
+ ];
+
+ ctx.clearRect(0, 0, 200, 200);
+ ctx.fillStyle = '#00f';
+ ctx.fillRect(0, 0, 200, 200);
+ ctx.fillStyle = '#000';
+
+ for (let i = 0; i < rows.length; i++) {
+ for (let j = 0; j < rows[i].length; j++) {
+ const bit = rows[i][j];
+ if (bit === '1') {
+ const x = 10 + j * 30;
+ const y = 20 + i * 30;
+ ctx.fillRect(x, y, 20, 20);
+ }
+ }
+ }
+ }
+
+ function drawWordClock(ctx) {
+ const now = new Date();
+ const hours = now.getHours();
+ const minutes = now.getMinutes();
+ const words = [
+ "IT IS",
+ "TWELVE",
+ "ONE",
+ "TWO",
+ "THREE",
+ "FOUR",
+ "FIVE",
+ "SIX",
+ "SEVEN",
+ "EIGHT",
+ "NINE",
+ "TEN",
+ "ELEVEN"
+ ];
+
+ let timeText = "IT IS";
+ if (minutes === 0) {
+ timeText += ` ${words[hours % 12]}`;
+ } else if (minutes < 15) {
+ timeText += " FIVE MINUTES PAST";
+ } else if (minutes < 30) {
+ timeText += ` ${Math.floor(minutes / 5)} FIVE MINUTES PAST`;
+ } else if (minutes === 30) {
+ timeText += " HALF PAST";
+ } else if (minutes < 45) {
+ timeText += ` ${Math.floor((60 - minutes) / 5)} FIVE MINUTES TO`;
+ } else {
+ timeText += ` ${Math.floor((60 - minutes) / 5)} FIVE MINUTES TO`;
+ }
+
+ if (minutes !== 0 && minutes !== 30) {
+ const hour = (hours % 12) + (minutes >= 30 ? 1 : 0);
+ timeText += ` ${words[hour % 12]}`;
+ }
+
+ ctx.clearRect(0, 0, 200, 200);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, 200, 200);
+ ctx.fillStyle = '#fff';
+ ctx.font = '18px monospace';
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+ ctx.fillText(timeText, 100, 100);
+ }
+
+ function updateClocks() {
+ const now = new Date();
+ const time = now.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', hour12: false});
+ document.getElementById('flip-clock').textContent = time;
+ document.getElementById('flip-clock-2').textContent = time;
+ document.getElementById('binary-clock').textContent = time;
+
+ drawAnalogClock(document.getElementById('analog-clock').getContext('2d'));
+ drawFlipClock(document.getElementById('flip-clock-canvas').getContext('2d'));
+ drawBinaryClock(document.getElementById('binary-clock-canvas').getContext('2d'));
+ drawWordClock(document.getElementById('word-clock-canvas').getContext('2d'));
+ }
+
+ setInterval(updateClocks, 1000);
+ updateClocks();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/9ba7a5497a50/qwen3-14b.png b/data/artifacts/9ba7a5497a50/qwen3-14b.png
new file mode 100644
index 0000000..7f5d1a8
Binary files /dev/null and b/data/artifacts/9ba7a5497a50/qwen3-14b.png differ
diff --git a/data/artifacts/a3958f97e739/claude-code.html b/data/artifacts/a3958f97e739/claude-code.html
new file mode 100644
index 0000000..823c5f2
--- /dev/null
+++ b/data/artifacts/a3958f97e739/claude-code.html
@@ -0,0 +1,228 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>One-Octave Piano</title>
+<style>
+ :root{
+ --bg:#12141a; --panel:#1d2129; --accent:#4fc3f7; --white:#f5f2ea; --black:#16181d;
+ }
+ *{box-sizing:border-box; -webkit-user-select:none; user-select:none;}
+ html,body{height:100%;}
+ body{
+ margin:0; background:radial-gradient(1200px 600px at 50% -10%, #232838, var(--bg));
+ font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
+ color:#e8eaf0; display:flex; align-items:center; justify-content:center; padding:16px;
+ }
+ .rig{width:100%; max-width:760px;}
+ h1{font-size:18px; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
+ text-align:center; margin:0 0 4px; color:#cfd6e4;}
+ .hint{text-align:center; font-size:12px; color:#7c8598; margin:0 0 14px;}
+ .controls{
+ display:flex; gap:14px; align-items:center; justify-content:center; flex-wrap:wrap;
+ background:var(--panel); border:1px solid #2b3040; border-radius:12px 12px 0 0;
+ padding:12px 16px;
+ }
+ .ctl{display:flex; align-items:center; gap:8px; font-size:13px; color:#aeb6c6;}
+ .wavegroup{display:flex; border:1px solid #343b4e; border-radius:8px; overflow:hidden;}
+ .wavegroup button{
+ background:#232838; color:#aeb6c6; border:none; padding:7px 14px; font-size:13px;
+ cursor:pointer; font-family:inherit;
+ }
+ .wavegroup button + button{border-left:1px solid #343b4e;}
+ .wavegroup button.on{background:var(--accent); color:#0b2531; font-weight:600;}
+ .toggle{
+ background:#232838; color:#aeb6c6; border:1px solid #343b4e; border-radius:8px;
+ padding:7px 14px; font-size:13px; cursor:pointer; font-family:inherit;
+ }
+ .toggle.on{background:#ffb74d; border-color:#ffb74d; color:#31210b; font-weight:600;}
+ .led{display:inline-block; width:8px; height:8px; border-radius:50%; background:#3a4152; margin-right:6px; vertical-align:1px;}
+ .toggle.on .led{background:#7a4d00;}
+ .piano-wrap{
+ background:var(--panel); border:1px solid #2b3040; border-top:none;
+ border-radius:0 0 12px 12px; padding:14px 16px 18px;
+ }
+ .piano{position:relative; height:230px; display:flex;}
+ .key.white{
+ flex:1; position:relative; margin:0 1px; background:linear-gradient(#ffffff,#e9e5da);
+ border:1px solid #b8b2a4; border-radius:0 0 7px 7px; cursor:pointer;
+ box-shadow:inset 0 -6px 0 rgba(0,0,0,.06);
+ display:flex; flex-direction:column; justify-content:flex-end; align-items:center;
+ padding-bottom:8px; color:#8a8474;
+ }
+ .key.white.lit{
+ background:linear-gradient(#bfe9ff,#7fd2fb);
+ border-color:#4aa3c9; color:#155a78;
+ box-shadow:inset 0 -4px 0 rgba(0,60,90,.15), 0 0 18px rgba(79,195,247,.55);
+ }
+ .key.black{
+ position:absolute; top:0; width:9%; height:60%;
+ background:linear-gradient(#3a3f4c,var(--black));
+ border:1px solid #000; border-radius:0 0 6px 6px; cursor:pointer; z-index:2;
+ display:flex; flex-direction:column; justify-content:flex-end; align-items:center;
+ padding-bottom:8px; color:#9aa2b4;
+ box-shadow:0 4px 8px rgba(0,0,0,.5);
+ }
+ .key.black.lit{
+ background:linear-gradient(#ffd79a,#ffb74d);
+ border-color:#c98a2e; color:#5c3d07;
+ box-shadow:0 0 18px rgba(255,183,77,.65);
+ }
+ .kbd{
+ font-size:11px; font-weight:700; border:1px solid currentColor; border-radius:4px;
+ padding:1px 5px; opacity:.75; margin-bottom:4px;
+ }
+ .nname{font-size:10px; opacity:.7;}
+ @media (max-width:520px){ .piano{height:170px;} .kbd{display:none;} }
+</style>
+</head>
+<body>
+<div class="rig">
+ <h1>One-Octave Piano</h1>
+ <p class="hint">Click the keys or use your computer keyboard (A–K white, W E T Y U black)</p>
+ <div class="controls">
+ <div class="ctl">Waveform
+ <div class="wavegroup" id="waves">
+ <button data-wave="sine" class="on">Sine</button>
+ <button data-wave="square">Square</button>
+ <button data-wave="sawtooth">Saw</button>
+ </div>
+ </div>
+ <button class="toggle" id="sustain"><span class="led"></span>Sustain Off</button>
+ </div>
+ <div class="piano-wrap"><div class="piano" id="piano"></div></div>
+</div>
+
+<script>
+(function(){
+ "use strict";
+
+ var NOTES = [
+ {n:"C4", f:261.63, k:"a", black:false},
+ {n:"C#4", f:277.18, k:"w", black:true, pos:0},
+ {n:"D4", f:293.66, k:"s", black:false},
+ {n:"D#4", f:311.13, k:"e", black:true, pos:1},
+ {n:"E4", f:329.63, k:"d", black:false},
+ {n:"F4", f:349.23, k:"f", black:false},
+ {n:"F#4", f:369.99, k:"t", black:true, pos:3},
+ {n:"G4", f:392.00, k:"g", black:false},
+ {n:"G#4", f:415.30, k:"y", black:true, pos:4},
+ {n:"A4", f:440.00, k:"h", black:false},
+ {n:"A#4", f:466.16, k:"u", black:true, pos:5},
+ {n:"B4", f:493.88, k:"j", black:false},
+ {n:"C5", f:523.25, k:"k", black:false}
+ ];
+ var WHITE_COUNT = 8;
+
+ var wave = "sine";
+ var sustain = false;
+ var ctx = null;
+ var active = {}; // note name -> {osc, gain, el}
+ var keyMap = {}; // keyboard char -> note object
+
+ // Envelope (seconds)
+ var ATK = 0.012, DEC = 0.18, SUS = 0.55, REL = 0.25, REL_SUSTAIN = 2.4;
+ var GAIN_BY_WAVE = { sine: 0.5, square: 0.18, sawtooth: 0.24 };
+
+ function audio(){
+ if(!ctx){
+ ctx = new (window.AudioContext || window.webkitAudioContext)();
+ }
+ if(ctx.state === "suspended") ctx.resume();
+ return ctx;
+ }
+
+ // ---- Build keyboard DOM ----
+ var piano = document.getElementById("piano");
+ NOTES.forEach(function(note){
+ var el = document.createElement("div");
+ el.className = "key " + (note.black ? "black" : "white");
+ el.innerHTML = '<span class="kbd">' + note.k.toUpperCase() + '</span><span class="nname">' + note.n + '</span>';
+ if(note.black){
+ // center the black key over the boundary between white key `pos` and `pos+1`
+ var boundary = (note.pos + 1) / WHITE_COUNT * 100;
+ el.style.left = "calc(" + boundary + "% - 4.5%)";
+ }
+ note.el = el;
+ keyMap[note.k] = note;
+ piano.appendChild(el);
+
+ el.addEventListener("mousedown", function(e){ e.preventDefault(); press(note); });
+ el.addEventListener("mouseup", function(){ release(note); });
+ el.addEventListener("mouseleave",function(){ release(note); });
+ el.addEventListener("touchstart",function(e){ e.preventDefault(); press(note); }, {passive:false});
+ el.addEventListener("touchend", function(e){ e.preventDefault(); release(note); }, {passive:false});
+ el.addEventListener("touchcancel",function(){ release(note); });
+ });
+
+ // ---- Sound ----
+ function press(note){
+ if(active[note.n]) return;
+ var ac = audio();
+ var t = ac.currentTime;
+ var osc = ac.createOscillator();
+ var gain = ac.createGain();
+ var peak = GAIN_BY_WAVE[wave];
+
+ osc.type = wave;
+ osc.frequency.value = note.f;
+
+ gain.gain.setValueAtTime(0.0001, t);
+ gain.gain.linearRampToValueAtTime(peak, t + ATK); // Attack
+ gain.gain.setTargetAtTime(peak * SUS, t + ATK, DEC / 3); // Decay -> Sustain
+
+ osc.connect(gain).connect(ac.destination);
+ osc.start(t);
+
+ active[note.n] = { osc: osc, gain: gain };
+ note.el.classList.add("lit");
+ }
+
+ function release(note){
+ var v = active[note.n];
+ if(!v) return;
+ delete active[note.n];
+ note.el.classList.remove("lit");
+
+ var t = ctx.currentTime;
+ var rel = sustain ? REL_SUSTAIN : REL;
+ v.gain.gain.cancelScheduledValues(t);
+ v.gain.gain.setValueAtTime(v.gain.gain.value, t);
+ v.gain.gain.setTargetAtTime(0.0001, t, rel / 4); // Release
+ v.osc.stop(t + rel + 0.1);
+ }
+
+ // ---- Computer keyboard ----
+ window.addEventListener("keydown", function(e){
+ if(e.repeat || e.metaKey || e.ctrlKey || e.altKey) return;
+ var note = keyMap[e.key.toLowerCase()];
+ if(note){ e.preventDefault(); press(note); }
+ });
+ window.addEventListener("keyup", function(e){
+ var note = keyMap[e.key.toLowerCase()];
+ if(note) release(note);
+ });
+ window.addEventListener("blur", function(){
+ NOTES.forEach(release);
+ });
+
+ // ---- Controls ----
+ var waveButtons = document.querySelectorAll("#waves button");
+ waveButtons.forEach(function(btn){
+ btn.addEventListener("click", function(){
+ wave = btn.dataset.wave;
+ waveButtons.forEach(function(b){ b.classList.toggle("on", b === btn); });
+ });
+ });
+
+ var sustainBtn = document.getElementById("sustain");
+ sustainBtn.addEventListener("click", function(){
+ sustain = !sustain;
+ sustainBtn.classList.toggle("on", sustain);
+ sustainBtn.innerHTML = '<span class="led"></span>Sustain ' + (sustain ? "On" : "Off");
+ });
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a3958f97e739/claude-code.png b/data/artifacts/a3958f97e739/claude-code.png
new file mode 100644
index 0000000..3fb3d84
Binary files /dev/null and b/data/artifacts/a3958f97e739/claude-code.png differ
diff --git a/data/artifacts/a3958f97e739/gemma3-12b.html b/data/artifacts/a3958f97e739/gemma3-12b.html
new file mode 100644
index 0000000..aef81e1
--- /dev/null
+++ b/data/artifacts/a3958f97e739/gemma3-12b.html
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>One-Octave Piano</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: #222; color: white; font-family: sans-serif; }
+ #piano { display: flex; width: 100%; height: 100vh; justify-content: center; align-items: center; }
+ .key { width: 40px; height: 150px; margin: 5px; border: 2px solid white; box-sizing: border-box; cursor: pointer; background-color: #333; transition: all 0.1s; }
+ .key.active { background-color: #fff; border-color: #000; }
+ #controls { position: absolute; top: 10px; left: 10px; display: flex; gap: 10px; align-items: center; }
+ #waveform-select { appearance: none; background-color: transparent; border: none; padding: 5px; width: 80px; color: white; outline: none; }
+ #sustain-toggle { appearance: none; -webkit-appearance: none; -moz-appearance: none; border: none; padding: 5px; background-color: #444; color: white; cursor: pointer; outline: none; }
+
+ </style>
+</head>
+<body>
+ <div id="piano"></div>
+ <div id="controls">
+ <label for="waveform-select">Waveform:</label>
+ <select id="waveform-select">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="saw">Sawtooth</option>
+ </select>
+ <label for="sustain-toggle">Sustain:</label>
+ <input type="checkbox" id="sustain-toggle">
+ </div>
+
+ <script>
+ const piano = document.getElementById('piano');
+ const waveformSelect = document.getElementById('waveform-select');
+ const sustainToggle = document.getElementById('sustain-toggle');
+
+ const notes = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5'];
+ const noteFrequencies = {
+ 'C4': 261.63, 'D4': 293.66, 'E4': 329.63, 'F4': 349.23,
+ 'G4': 392.00, 'A4': 440.00, 'B4': 493.88, 'C5': 523.25
+ };
+
+ let audioContext = null;
+ let oscillator = null;
+ let envelope = null;
+ let gainNode = null;
+
+ function init() {
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
+ gainNode = audioContext.createGain();
+ gainNode.connect(audioContext.destination);
+ gainNode.gain.setValueAtTime(0, audioContext.currentTime);
+
+ // ADSR Envelope
+ envelope = {
+ attack: 0.1,
+ decay: 0.2,
+ sustainLevel: 0.7,
+ release: 0.3
+ };
+
+ createKeys();
+ }
+
+ function createKeys() {
+ notes.forEach(note => {
+ const key = document.createElement('div');
+ key.classList.add('key');
+ key.dataset.note = note;
+ piano.appendChild(key);
+
+ key.addEventListener('mousedown', playNote);
+ key.addEventListener('mouseup', stopNote);
+ key.addEventListener('mouseout', stopNote); // Prevent accidental notes
+ key.addEventListener('click', playNote); // For touch devices
+ });
+ }
+
+
+ function playNote(event) {
+ const key = event.target;
+ const note = key.dataset.note;
+
+ if (audioContext === null) return;
+
+ oscillator = audioContext.createOscillator();
+ oscillator.type = waveformSelect.value;
+ oscillator.frequency.setValueAtTime(noteFrequencies[note], audioContext.currentTime);
+ oscillator.connect(gainNode);
+ oscillator.start(audioContext.currentTime);
+
+ // ADSR Envelope
+ const now = audioContext.currentTime;
+ gainNode.gain.setValueAtTime(0, now);
+ gainNode.gain.linearRampToValueAtTime(envelope.sustainLevel, now + envelope.attack);
+ if (envelope.decay > 0) {
+ gainNode.gain.linearRampToValueAtTime(envelope.sustainLevel * envelope.decay, now + envelope.attack + envelope.decay);
+ }
+
+ key.classList.add('active');
+
+ if (sustainToggle.checked) {
+ oscillator.stop(); // Stop oscillator and start again after release
+ setTimeout(() => {
+ oscillator = audioContext.createOscillator();
+ oscillator.type = waveformSelect.value;
+ oscillator.frequency.setValueAtTime(noteFrequencies[note], audioContext.currentTime);
+ oscillator.connect(gainNode);
+ oscillator.start(audioContext.currentTime);
+
+ const now2 = audioContext.currentTime;
+ gainNode.gain.setValueAtTime(0, now2);
+ gainNode.gain.linearRampToValueAtTime(envelope.sustainLevel, now2 + envelope.attack);
+ if (envelope.decay > 0) {
+ gainNode.gain.linearRampToValueAtTime(envelope.sustainLevel * envelope.decay, now2 + envelope.attack + envelope.decay);
+ }
+
+ }, 100); // Small delay to avoid immediate retriggering
+ }
+
+ }
+
+
+ function stopNote(event) {
+ const key = event.target;
+ const note = key.dataset.note;
+
+ if (audioContext === null || oscillator === null) return;
+
+ key.classList.remove('active');
+
+ // Release Envelope
+ const now = audioContext.currentTime;
+ gainNode.gain.cancelScheduledValues(now); // Important to cancel existing ramps
+ gainNode.gain.linearRampToValueAtTime(0, now + envelope.release);
+
+
+ if (oscillator) {
+ oscillator.stop();
+ oscillator = null;
+ }
+
+ }
+
+
+
+ waveformSelect.addEventListener('change', () => {
+ if (oscillator) {
+ oscillator.type = waveformSelect.value;
+ }
+ });
+
+ sustainToggle.addEventListener('change', () => {
+ // No immediate action needed, playNote handles sustain behavior
+ });
+
+
+ init();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a3958f97e739/gemma3-12b.png b/data/artifacts/a3958f97e739/gemma3-12b.png
new file mode 100644
index 0000000..88bb186
Binary files /dev/null and b/data/artifacts/a3958f97e739/gemma3-12b.png differ
diff --git a/data/artifacts/a3958f97e739/gpt.html b/data/artifacts/a3958f97e739/gpt.html
new file mode 100644
index 0000000..cd91589
--- /dev/null
+++ b/data/artifacts/a3958f97e739/gpt.html
@@ -0,0 +1,544 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>One-Octave WebAudio Piano</title>
+<style>
+ :root {
+ --white-key-width: 50px;
+ --white-key-height: 200px;
+ --black-key-width: 32px;
+ --black-key-height: 120px;
+ --active-color: #ffd54f;
+ }
+
+ * { box-sizing: border-box; }
+
+ body {
+ margin: 0;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background: #111;
+ color: #eee;
+ user-select: none;
+ }
+
+ h1 {
+ margin: 12px 0 4px;
+ font-size: 20px;
+ font-weight: 600;
+ letter-spacing: 0.04em;
+ }
+
+ .subtitle {
+ font-size: 11px;
+ opacity: 0.7;
+ margin-bottom: 10px;
+ }
+
+ .controls {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ margin-bottom: 10px;
+ flex-wrap: wrap;
+ justify-content: center;
+ font-size: 13px;
+ }
+
+ .control-group {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ select {
+ background: #222;
+ border: 1px solid #555;
+ color: #eee;
+ padding: 2px 6px;
+ border-radius: 4px;
+ font-size: 13px;
+ }
+
+ label {
+ cursor: pointer;
+ }
+
+ input[type="checkbox"] {
+ accent-color: #ffc107;
+ cursor: pointer;
+ }
+
+ .piano-wrapper {
+ position: relative;
+ padding: 10px 10px 16px;
+ background: #222;
+ border-radius: 10px;
+ box-shadow: 0 8px 20px rgba(0,0,0,0.6);
+ }
+
+ .piano {
+ position: relative;
+ display: inline-block;
+ }
+
+ .row-hints {
+ display: flex;
+ justify-content: center;
+ margin-top: 6px;
+ font-size: 11px;
+ opacity: 0.7;
+ }
+
+ .row-hints span {
+ margin: 0 3px;
+ }
+
+ .white-keys {
+ display: flex;
+ position: relative;
+ z-index: 1;
+ }
+
+ .key {
+ position: relative;
+ border-radius: 0 0 4px 4px;
+ cursor: pointer;
+ display: flex;
+ justify-content: center;
+ align-items: flex-end;
+ padding-bottom: 6px;
+ font-size: 11px;
+ font-weight: 500;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ user-select: none;
+ transition: transform 0.03s ease-out, background 0.08s ease-out, box-shadow 0.08s ease-out;
+ }
+
+ .white-key {
+ width: var(--white-key-width);
+ height: var(--white-key-height);
+ background: linear-gradient(#fafafa, #e9e9e9);
+ border: 1px solid #666;
+ box-shadow: 0 3px 0 #000;
+ color: #111;
+ z-index: 1;
+ }
+
+ .white-key.active {
+ background: linear-gradient(#ffe082, #ffca28);
+ box-shadow: 0 1px 0 #222 inset;
+ transform: translateY(2px);
+ }
+
+ .black-key {
+ position: absolute;
+ width: var(--black-key-width);
+ height: var(--black-key-height);
+ background: linear-gradient(#333, #000);
+ border-radius: 0 0 4px 4px;
+ border: 1px solid #000;
+ margin-left: calc(var(--black-key-width) / -2);
+ top: 0;
+ z-index: 2;
+ color: #eee;
+ box-shadow: 0 4px 4px rgba(0,0,0,0.7);
+ font-size: 9px;
+ }
+
+ .black-key.active {
+ background: linear-gradient(#ffb300, #ff8f00);
+ box-shadow: 0 1px 2px rgba(0,0,0,0.8);
+ transform: translateY(2px);
+ }
+
+ .key-label {
+ pointer-events: none;
+ text-align: center;
+ }
+
+ .note-name {
+ display: block;
+ font-size: 10px;
+ }
+
+ .kbd-name {
+ opacity: 0.75;
+ font-size: 9px;
+ }
+
+ /* Positioning black keys over white keys */
+ .black-Csharp { left: calc(var(--white-key-width) * 1); }
+ .black-Dsharp { left: calc(var(--white-key-width) * 2); }
+ .black-Fsharp { left: calc(var(--white-key-width) * 4); }
+ .black-Gsharp { left: calc(var(--white-key-width) * 5); }
+ .black-Asharp { left: calc(var(--white-key-width) * 6); }
+
+ .footer {
+ margin-top: 10px;
+ font-size: 10px;
+ opacity: 0.5;
+ }
+</style>
+</head>
+<body>
+<h1>WebAudio Piano</h1>
+<div class="subtitle">Click or use the keyboard rows to play one octave</div>
+
+<div class="controls">
+ <div class="control-group">
+ <span>Waveform:</span>
+ <select id="waveform">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="sawtooth">Saw</option>
+ </select>
+ </div>
+ <div class="control-group">
+ <label>
+ <input type="checkbox" id="sustain">
+ Sustain (hold notes)
+ </label>
+ </div>
+</div>
+
+<div class="piano-wrapper">
+ <div class="piano" id="piano">
+ <!-- White keys -->
+ <div class="white-keys">
+ <div class="key white-key" data-note="C4" data-key="a">
+ <div class="key-label">
+ <span class="note-name">C</span>
+ <span class="kbd-name">A</span>
+ </div>
+ </div>
+ <div class="key white-key" data-note="D4" data-key="s">
+ <div class="key-label">
+ <span class="note-name">D</span>
+ <span class="kbd-name">S</span>
+ </div>
+ </div>
+ <div class="key white-key" data-note="E4" data-key="d">
+ <div class="key-label">
+ <span class="note-name">E</span>
+ <span class="kbd-name">D</span>
+ </div>
+ </div>
+ <div class="key white-key" data-note="F4" data-key="f">
+ <div class="key-label">
+ <span class="note-name">F</span>
+ <span class="kbd-name">F</span>
+ </div>
+ </div>
+ <div class="key white-key" data-note="G4" data-key="g">
+ <div class="key-label">
+ <span class="note-name">G</span>
+ <span class="kbd-name">G</span>
+ </div>
+ </div>
+ <div class="key white-key" data-note="A4" data-key="h">
+ <div class="key-label">
+ <span class="note-name">A</span>
+ <span class="kbd-name">H</span>
+ </div>
+ </div>
+ <div class="key white-key" data-note="B4" data-key="j">
+ <div class="key-label">
+ <span class="note-name">B</span>
+ <span class="kbd-name">J</span>
+ </div>
+ </div>
+ <div class="key white-key" data-note="C5" data-key="k">
+ <div class="key-label">
+ <span class="note-name">C</span>
+ <span class="kbd-name">K</span>
+ </div>
+ </div>
+ </div>
+
+ <!-- Black keys -->
+ <div class="key black-key black-Csharp" data-note="C#4" data-key="w">
+ <div class="key-label">
+ <span class="note-name">C♯</span>
+ <span class="kbd-name">W</span>
+ </div>
+ </div>
+ <div class="key black-key black-Dsharp" data-note="D#4" data-key="e">
+ <div class="key-label">
+ <span class="note-name">D♯</span>
+ <span class="kbd-name">E</span>
+ </div>
+ </div>
+ <div class="key black-key black-Fsharp" data-note="F#4" data-key="t">
+ <div class="key-label">
+ <span class="note-name">F♯</span>
+ <span class="kbd-name">T</span>
+ </div>
+ </div>
+ <div class="key black-key black-Gsharp" data-note="G#4" data-key="y">
+ <div class="key-label">
+ <span class="note-name">G♯</span>
+ <span class="kbd-name">Y</span>
+ </div>
+ </div>
+ <div class="key black-key black-Asharp" data-note="A#4" data-key="u">
+ <div class="key-label">
+ <span class="note-name">A♯</span>
+ <span class="kbd-name">U</span>
+ </div>
+ </div>
+ </div>
+
+ <div class="row-hints">
+ <span>Row 1 (W E T Y U) → black keys</span> |
+ <span>Row 2 (A S D F G H J K) → white keys</span>
+ </div>
+</div>
+
+<div class="footer">ADSR envelope, WebAudio oscillators, no samples or libraries.</div>
+
+<script>
+(function() {
+ // Frequencies for notes (equal temperament, A4 = 440Hz)
+ const noteFrequencies = {
+ "C4": 261.63,
+ "C#4": 277.18,
+ "D4": 293.66,
+ "D#4": 311.13,
+ "E4": 329.63,
+ "F4": 349.23,
+ "F#4": 369.99,
+ "G4": 392.00,
+ "G#4": 415.30,
+ "A4": 440.00,
+ "A#4": 466.16,
+ "B4": 493.88,
+ "C5": 523.25
+ };
+
+ // ADSR envelope settings (seconds)
+ const ADSR = {
+ attack: 0.015,
+ decay: 0.12,
+ sustainLevel: 0.6,
+ release: 0.25
+ };
+
+ let audioCtx = null;
+ const activeVoices = new Map(); // note -> {osc, gain}
+ const heldKeys = new Set(); // keyboard keys currently held
+
+ const waveformSelect = document.getElementById('waveform');
+ const sustainCheckbox = document.getElementById('sustain');
+ const piano = document.getElementById('piano');
+ const keys = Array.from(piano.querySelectorAll('.key'));
+
+ function ensureAudioContext() {
+ if (!audioCtx) {
+ audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ }
+ if (audioCtx.state === 'suspended') {
+ audioCtx.resume();
+ }
+ }
+
+ function getWaveform() {
+ return waveformSelect.value || 'sine';
+ }
+
+ function now() {
+ return audioCtx.currentTime;
+ }
+
+ function startNote(note) {
+ if (!noteFrequencies[note]) return;
+
+ ensureAudioContext();
+
+ // If already active, retrigger the attack
+ const existing = activeVoices.get(note);
+ if (existing) {
+ const t0 = now();
+ const g = existing.gain.gain;
+ g.cancelScheduledValues(t0);
+ const current = g.value;
+ g.setValueAtTime(current, t0);
+ g.linearRampToValueAtTime(1.0, t0 + ADSR.attack);
+ g.linearRampToValueAtTime(ADSR.sustainLevel, t0 + ADSR.attack + ADSR.decay);
+ return;
+ }
+
+ const osc = audioCtx.createOscillator();
+ const gainNode = audioCtx.createGain();
+ osc.type = getWaveform();
+ osc.frequency.value = noteFrequencies[note];
+
+ gainNode.gain.setValueAtTime(0, now());
+ osc.connect(gainNode);
+ gainNode.connect(audioCtx.destination);
+
+ const t0 = now();
+ gainNode.gain.cancelScheduledValues(t0);
+ gainNode.gain.setValueAtTime(0, t0);
+ gainNode.gain.linearRampToValueAtTime(1.0, t0 + ADSR.attack);
+ gainNode.gain.linearRampToValueAtTime(ADSR.sustainLevel, t0 + ADSR.attack + ADSR.decay);
+
+ osc.start();
+
+ activeVoices.set(note, { osc, gain: gainNode });
+ }
+
+ function stopNote(note) {
+ const voice = activeVoices.get(note);
+ if (!voice) return;
+
+ const { osc, gain } = voice;
+ const t0 = now();
+ gain.gain.cancelScheduledValues(t0);
+ const current = gain.gain.value;
+ gain.gain.setValueAtTime(current, t0);
+ gain.gain.linearRampToValueAtTime(0, t0 + ADSR.release);
+
+ setTimeout(() => {
+ osc.stop();
+ osc.disconnect();
+ gain.disconnect();
+ }, (ADSR.release + 0.05) * 1000);
+
+ activeVoices.delete(note);
+ }
+
+ function pressVisual(note) {
+ keys.forEach(k => {
+ if (k.dataset.note === note) {
+ k.classList.add('active');
+ }
+ });
+ }
+
+ function releaseVisual(note) {
+ keys.forEach(k => {
+ if (k.dataset.note === note) {
+ k.classList.remove('active');
+ }
+ });
+ }
+
+ function noteFromKeyChar(char) {
+ char = char.toLowerCase();
+ const key = keys.find(k => (k.dataset.key || '').toLowerCase() === char);
+ return key ? key.dataset.note : null;
+ }
+
+ // Mouse / touch handling
+ function startEventForKeyElement(el) {
+ const note = el.dataset.note;
+ if (!note) return;
+ startNote(note);
+ pressVisual(note);
+ }
+
+ function endEventForKeyElement(el) {
+ const note = el.dataset.note;
+ if (!note) return;
+ if (!sustainCheckbox.checked) {
+ stopNote(note);
+ releaseVisual(note);
+ } else {
+ // keep audio, but release visual immediately
+ releaseVisual(note);
+ }
+ }
+
+ keys.forEach(key => {
+ key.addEventListener('mousedown', e => {
+ e.preventDefault();
+ startEventForKeyElement(key);
+ });
+ key.addEventListener('mouseup', e => {
+ e.preventDefault();
+ endEventForKeyElement(key);
+ });
+ key.addEventListener('mouseleave', e => {
+ if (e.buttons & 1) {
+ endEventForKeyElement(key);
+ }
+ });
+ key.addEventListener('touchstart', e => {
+ e.preventDefault();
+ startEventForKeyElement(key);
+ }, { passive: false });
+ key.addEventListener('touchend', e => {
+ e.preventDefault();
+ endEventForKeyElement(key);
+ }, { passive: false });
+ });
+
+ // Keyboard handling
+ window.addEventListener('keydown', e => {
+ const key = e.key.toLowerCase();
+ if (e.repeat) return; // avoid repeated retrigger
+ const note = noteFromKeyChar(key);
+ if (!note) return;
+ e.preventDefault();
+ if (heldKeys.has(key)) return;
+ heldKeys.add(key);
+ startNote(note);
+ pressVisual(note);
+ });
+
+ window.addEventListener('keyup', e => {
+ const key = e.key.toLowerCase();
+ const note = noteFromKeyChar(key);
+ if (!note) return;
+ e.preventDefault();
+ heldKeys.delete(key);
+ if (!sustainCheckbox.checked) {
+ stopNote(note);
+ releaseVisual(note);
+ } else {
+ releaseVisual(note);
+ }
+ });
+
+ // Change waveform of currently playing notes immediately
+ waveformSelect.addEventListener('change', () => {
+ const wave = getWaveform();
+ activeVoices.forEach(voice => {
+ voice.osc.type = wave;
+ });
+ });
+
+ // When sustain toggled off, release all notes not physically held
+ sustainCheckbox.addEventListener('change', () => {
+ if (!sustainCheckbox.checked) {
+ const heldNotes = new Set();
+ heldKeys.forEach(k => {
+ const n = noteFromKeyChar(k);
+ if (n) heldNotes.add(n);
+ });
+ Array.from(activeVoices.keys()).forEach(note => {
+ if (!heldNotes.has(note)) {
+ stopNote(note);
+ releaseVisual(note);
+ }
+ });
+ }
+ });
+
+ // Handle window blur: release everything
+ window.addEventListener('blur', () => {
+ heldKeys.clear();
+ Array.from(activeVoices.keys()).forEach(note => {
+ stopNote(note);
+ releaseVisual(note);
+ });
+ });
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a3958f97e739/gpt.png b/data/artifacts/a3958f97e739/gpt.png
new file mode 100644
index 0000000..b5e5d3f
Binary files /dev/null and b/data/artifacts/a3958f97e739/gpt.png differ
diff --git a/data/artifacts/a3958f97e739/grok.html b/data/artifacts/a3958f97e739/grok.html
new file mode 100644
index 0000000..84201da
--- /dev/null
+++ b/data/artifacts/a3958f97e739/grok.html
@@ -0,0 +1,393 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>One-Octave Piano</title>
+<style>
+ * { box-sizing: border-box; margin: 0; padding: 0; }
+ body {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(145deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
+ font-family: "Segoe UI", system-ui, sans-serif;
+ color: #e8e8e8;
+ user-select: none;
+ -webkit-user-select: none;
+ padding: 24px;
+ }
+ h1 {
+ font-weight: 300;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ font-size: 1.1rem;
+ margin-bottom: 20px;
+ opacity: 0.85;
+ }
+ .controls {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 16px 28px;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 28px;
+ background: rgba(255,255,255,0.06);
+ border: 1px solid rgba(255,255,255,0.1);
+ border-radius: 12px;
+ padding: 14px 22px;
+ }
+ .controls label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 0.9rem;
+ cursor: pointer;
+ }
+ .controls select {
+ background: #1a1a2e;
+ color: #e8e8e8;
+ border: 1px solid rgba(255,255,255,0.25);
+ border-radius: 6px;
+ padding: 6px 10px;
+ font-size: 0.9rem;
+ cursor: pointer;
+ outline: none;
+ }
+ .controls select:focus { border-color: #e94560; }
+ .toggle {
+ position: relative;
+ width: 42px;
+ height: 24px;
+ background: #333;
+ border-radius: 12px;
+ cursor: pointer;
+ transition: background 0.2s;
+ border: none;
+ flex-shrink: 0;
+ }
+ .toggle.on { background: #e94560; }
+ .toggle::after {
+ content: "";
+ position: absolute;
+ top: 3px;
+ left: 3px;
+ width: 18px;
+ height: 18px;
+ background: #fff;
+ border-radius: 50%;
+ transition: transform 0.2s;
+ }
+ .toggle.on::after { transform: translateX(18px); }
+ .piano-wrap {
+ position: relative;
+ display: flex;
+ padding: 16px 12px 20px;
+ background: linear-gradient(180deg, #2c2c2c, #1a1a1a);
+ border-radius: 12px;
+ box-shadow:
+ 0 20px 40px rgba(0,0,0,0.5),
+ inset 0 1px 0 rgba(255,255,255,0.08);
+ }
+ .white-keys {
+ display: flex;
+ gap: 4px;
+ position: relative;
+ }
+ .key {
+ position: relative;
+ border: none;
+ cursor: pointer;
+ outline: none;
+ transition: background 0.06s, box-shadow 0.06s, transform 0.06s;
+ }
+ .key.white {
+ width: 56px;
+ height: 220px;
+ background: linear-gradient(180deg, #fafafa 0%, #e8e8e8 100%);
+ border-radius: 0 0 6px 6px;
+ box-shadow: 0 4px 0 #bbb, 0 6px 10px rgba(0,0,0,0.3);
+ color: #555;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-end;
+ padding-bottom: 12px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ z-index: 1;
+ }
+ .key.white:hover { background: linear-gradient(180deg, #fff 0%, #f0f0f0 100%); }
+ .key.white.active {
+ background: linear-gradient(180deg, #ffe0e6 0%, #ffb3c1 100%);
+ box-shadow: 0 1px 0 #bbb, 0 2px 6px rgba(0,0,0,0.25);
+ transform: translateY(3px);
+ }
+ .key.black {
+ position: absolute;
+ width: 36px;
+ height: 140px;
+ background: linear-gradient(180deg, #444 0%, #111 60%, #000 100%);
+ border-radius: 0 0 4px 4px;
+ box-shadow: 0 4px 0 #000, 0 6px 8px rgba(0,0,0,0.5);
+ z-index: 2;
+ color: #aaa;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-end;
+ padding-bottom: 10px;
+ font-size: 0.65rem;
+ font-weight: 600;
+ }
+ .key.black:hover { background: linear-gradient(180deg, #555 0%, #222 60%, #111 100%); }
+ .key.black.active {
+ background: linear-gradient(180deg, #e94560 0%, #8b1a30 100%);
+ box-shadow: 0 1px 0 #000, 0 2px 4px rgba(0,0,0,0.4);
+ transform: translateY(3px);
+ color: #fff;
+ }
+ .hint {
+ margin-top: 22px;
+ font-size: 0.8rem;
+ opacity: 0.55;
+ text-align: center;
+ line-height: 1.6;
+ }
+ .hint kbd {
+ display: inline-block;
+ background: rgba(255,255,255,0.1);
+ border: 1px solid rgba(255,255,255,0.2);
+ border-radius: 4px;
+ padding: 1px 6px;
+ font-family: inherit;
+ font-size: 0.78rem;
+ }
+</style>
+</head>
+<body>
+ <h1>One-Octave Piano</h1>
+ <div class="controls">
+ <label>
+ Waveform
+ <select id="wave">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="sawtooth">Saw</option>
+ </select>
+ </label>
+ <label>
+ Sustain
+ <button type="button" class="toggle" id="sustainBtn" aria-pressed="false" title="Toggle sustain"></button>
+ </label>
+ </div>
+ <div class="piano-wrap" id="piano"></div>
+ <p class="hint">
+ White keys: <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> <kbd>F</kbd> <kbd>G</kbd> <kbd>H</kbd> <kbd>J</kbd> <kbd>K</kbd><br>
+ Black keys: <kbd>W</kbd> <kbd>E</kbd> <kbd>T</kbd> <kbd>Y</kbd> <kbd>U</kbd>
+ </p>
+<script>
+(function () {
+ const NOTES = [
+ { name: "C4", freq: 261.63, key: "a", black: false },
+ { name: "C#4", freq: 277.18, key: "w", black: true },
+ { name: "D4", freq: 293.66, key: "s", black: false },
+ { name: "D#4", freq: 311.13, key: "e", black: true },
+ { name: "E4", freq: 329.63, key: "d", black: false },
+ { name: "F4", freq: 349.23, key: "f", black: false },
+ { name: "F#4", freq: 369.99, key: "t", black: true },
+ { name: "G4", freq: 392.00, key: "g", black: false },
+ { name: "G#4", freq: 415.30, key: "y", black: true },
+ { name: "A4", freq: 440.00, key: "h", black: false },
+ { name: "A#4", freq: 466.16, key: "u", black: true },
+ { name: "B4", freq: 493.88, key: "j", black: false },
+ { name: "C5", freq: 523.25, key: "k", black: false }
+ ];
+
+ const ADSR = { attack: 0.02, decay: 0.12, sustain: 0.65, release: 0.35 };
+ const WHITE_W = 56, WHITE_GAP = 4, BLACK_W = 36;
+
+ let audioCtx = null;
+ let sustainOn = false;
+ const active = new Map();
+
+ const waveSelect = document.getElementById("wave");
+ const sustainBtn = document.getElementById("sustainBtn");
+ const pianoEl = document.getElementById("piano");
+
+ function ensureCtx() {
+ if (!audioCtx) {
+ audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ }
+ if (audioCtx.state === "suspended") audioCtx.resume();
+ return audioCtx;
+ }
+
+ function playNote(noteId) {
+ const note = NOTES[noteId];
+ if (!note) return;
+ if (active.has(noteId)) stopNote(noteId, true);
+
+ const ctx = ensureCtx();
+ const now = ctx.currentTime;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+
+ osc.type = waveSelect.value;
+ osc.frequency.setValueAtTime(note.freq, now);
+
+ const peak = 0.28;
+ gain.gain.setValueAtTime(0.0001, now);
+ gain.gain.exponentialRampToValueAtTime(peak, now + ADSR.attack);
+ gain.gain.exponentialRampToValueAtTime(
+ Math.max(peak * ADSR.sustain, 0.0001),
+ now + ADSR.attack + ADSR.decay
+ );
+
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.start(now);
+
+ active.set(noteId, { osc, gain, released: false });
+ const el = pianoEl.querySelector('[data-id="' + noteId + '"]');
+ if (el) el.classList.add("active");
+ }
+
+ function stopNote(noteId, immediate) {
+ const voice = active.get(noteId);
+ if (!voice || voice.released) return;
+
+ if (sustainOn && !immediate) return;
+
+ voice.released = true;
+ const ctx = audioCtx;
+ const now = ctx.currentTime;
+ const g = voice.gain.gain;
+ g.cancelScheduledValues(now);
+ g.setValueAtTime(Math.max(g.value, 0.0001), now);
+ g.exponentialRampToValueAtTime(0.0001, now + (immediate ? 0.03 : ADSR.release));
+ voice.osc.stop(now + (immediate ? 0.04 : ADSR.release + 0.02));
+
+ setTimeout(function () {
+ try { voice.osc.disconnect(); voice.gain.disconnect(); } catch (e) {}
+ if (active.get(noteId) === voice) active.delete(noteId);
+ }, (immediate ? 60 : (ADSR.release + 0.05) * 1000));
+
+ const el = pianoEl.querySelector('[data-id="' + noteId + '"]');
+ if (el) el.classList.remove("active");
+ }
+
+ function releaseAllSustained() {
+ active.forEach(function (voice, id) {
+ if (!voice.released) stopNote(id, false);
+ });
+ }
+
+ // Build piano DOM
+ const whiteKeysEl = document.createElement("div");
+ whiteKeysEl.className = "white-keys";
+ pianoEl.appendChild(whiteKeysEl);
+
+ let whiteIndex = 0;
+ const blackPositions = [];
+
+ NOTES.forEach(function (note, i) {
+ if (!note.black) {
+ const btn = document.createElement("button");
+ btn.type = "button";
+ btn.className = "key white";
+ btn.dataset.id = i;
+ btn.innerHTML = note.name.replace(/[0-9]/g, "") + "<br><small>" + note.key.toUpperCase() + "</small>";
+ whiteKeysEl.appendChild(btn);
+ whiteIndex++;
+ } else {
+ blackPositions.push({ id: i, afterWhite: whiteIndex - 1, note: note });
+ }
+ });
+
+ // Place black keys absolutely relative to piano-wrap
+ // White key centers: each white is WHITE_W + gap, first starts at padding 12px
+ const padLeft = 12;
+ blackPositions.forEach(function (bp) {
+ const btn = document.createElement("button");
+ btn.type = "button";
+ btn.className = "key black";
+ btn.dataset.id = bp.id;
+ btn.textContent = bp.note.key.toUpperCase();
+ // Position: right edge of the white key it follows, centered on the gap
+ const left =
+ padLeft +
+ (bp.afterWhite + 1) * (WHITE_W + WHITE_GAP) -
+ WHITE_GAP / 2 -
+ BLACK_W / 2;
+ btn.style.left = left + "px";
+ btn.style.top = "16px";
+ pianoEl.appendChild(btn);
+ });
+
+ // Pointer events
+ function bindKeyEl(el) {
+ const id = parseInt(el.dataset.id, 10);
+ el.addEventListener("pointerdown", function (e) {
+ e.preventDefault();
+ el.setPointerCapture(e.pointerId);
+ playNote(id);
+ });
+ el.addEventListener("pointerup", function () { stopNote(id, false); });
+ el.addEventListener("pointercancel", function () { stopNote(id, false); });
+ el.addEventListener("pointerleave", function (e) {
+ if (e.buttons === 0) stopNote(id, false);
+ });
+ }
+ pianoEl.querySelectorAll(".key").forEach(bindKeyEl);
+
+ // Keyboard
+ const keyToId = {};
+ NOTES.forEach(function (n, i) { keyToId[n.key] = i; });
+ const held = new Set();
+
+ window.addEventListener("keydown", function (e) {
+ if (e.repeat) return;
+ const k = e.key.toLowerCase();
+ if (k in keyToId && !held.has(k)) {
+ held.add(k);
+ playNote(keyToId[k]);
+ e.preventDefault();
+ }
+ });
+ window.addEventListener("keyup", function (e) {
+ const k = e.key.toLowerCase();
+ if (k in keyToId) {
+ held.delete(k);
+ stopNote(keyToId[k], false);
+ e.preventDefault();
+ }
+ });
+
+ // Sustain toggle
+ sustainBtn.addEventListener("click", function () {
+ sustainOn = !sustainOn;
+ sustainBtn.classList.toggle("on", sustainOn);
+ sustainBtn.setAttribute("aria-pressed", sustainOn ? "true" : "false");
+ if (!sustainOn) {
+ // Release notes whose keys are no longer held
+ active.forEach(function (voice, id) {
+ const note = NOTES[id];
+ if (!held.has(note.key) && !voice.released) {
+ // Check if pointer might still be down — release anyway on sustain off
+ stopNote(id, false);
+ }
+ });
+ }
+ });
+
+ // Waveform change for new notes only (existing keep their type)
+ waveSelect.addEventListener("change", function () {
+ // no-op for live voices; new notes pick up the value
+ });
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a3958f97e739/grok.png b/data/artifacts/a3958f97e739/grok.png
new file mode 100644
index 0000000..20bd8e0
Binary files /dev/null and b/data/artifacts/a3958f97e739/grok.png differ
diff --git a/data/artifacts/a3958f97e739/hermes3-8b.html b/data/artifacts/a3958f97e739/hermes3-8b.html
new file mode 100644
index 0000000..e3d08f7
--- /dev/null
+++ b/data/artifacts/a3958f97e739/hermes3-8b.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Playable Piano</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ text-align: center;
+ }
+
+ .key {
+ display: inline-block;
+ width: 40px;
+ height: 100px;
+ margin: 5px;
+ background-color: #ccc;
+ position: relative;
+ cursor: pointer;
+ }
+
+ .light {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 50%;
+ background-color: #888;
+ }
+
+ .waveform-selector {
+ margin-top: 20px;
+ }
+
+ .sustain-toggle {
+ margin-top: 10px;
+ }
+ </style>
+</head>
+<body>
+ <h1>Playable Piano</h1>
+
+ <div id="keyboard" class="key"></div>
+ <button id="c4" class="key">C4</button>
+ <button id="d4" class="key">D4</button>
+ <button id="e4" class="key">E4</button>
+ <button id="f4" class="key">F4</button>
+ <button id="g4" class="key">G4</button>
+ <button id="a4" class="key">A4</button>
+ <button id="b4" class="key">B4</button>
+
+ <div class="waveform-selector">
+ <label for="sine-wave">Sine:</label>
+ <input type="radio" id="sine-wave" name="waveform" checked>
+
+ <label for="square-wave">Square:</label>
+ <input type="radio" id="square-wave" name="waveform">
+
+ <label for="saw-wave">Saw:</label>
+ <input type="radio" id="saw-wave" name="waveform">
+ </div>
+
+ <div class="sustain-toggle">
+ <label for="sustain-toggle">Sustain:</label>
+ <input type="checkbox" id="sustain-toggle" disabled>
+ </div>
+
+ <script>
+ const context = new AudioContext();
+
+ document.querySelectorAll('.key').forEach(key => {
+ key.addEventListener('click', playNote);
+
+ const light = key.querySelector('.light');
+ const noteId = key.id;
+
+ key.addEventListener('mouseover', () => light.style.opacity = '1');
+ key.addEventListener('mouseout', () => light.style.opacity = '0.5');
+ });
+
+ document.querySelectorAll('[name="waveform"]').forEach(radio => {
+ radio.addEventListener('change', selectWaveform);
+ });
+
+ function playNote(event) {
+ const keyElement = event.target;
+ const noteId = keyElement.id;
+
+ switch (noteId) {
+ case 'c4':
+ playAudioContext(context, 261.63);
+ break;
+ case 'd4':
+ playAudioContext(context, 293.66);
+ break;
+ case 'e4':
+ playAudioContext(context, 327.02);
+ break;
+ case 'f4':
+ playAudioContext(context, 349.52);
+ break;
+ case 'g4':
+ playAudioContext(context, 392.00);
+ break;
+ case 'a4':
+ playAudioContext(context, 440.00);
+ break;
+ case 'b4':
+ playAudioContext(context, 493.88);
+ break;
+ }
+ }
+
+ function playAudioContext(audioCtx, frequency) {
+ const oscillator = audioCtx.createOscillator();
+ const gainNode = audioCtx.createGain();
+
+ oscillator.connect(gainNode);
+ gainNode.connect(audioCtx.destination);
+
+ oscillator.type = document.querySelector('input[name="waveform"]:checked).id;
+ oscillator.frequency.value = frequency;
+
+ const attackTime = 0.01;
+ const decayTime = 0.3;
+ const sustainLevel = 0.7;
+ const releaseTime = 0.2;
+
+ gainNode.gain.exponentialRampToValueAtTime(1, audioCtx.currentTime);
+ gainNode.gain.exponentialRampToValueAtTime(sustainLevel, audioCtx.currentTime + attackTime);
+ gainNode.gain.exponentialRampToValueAtTime(0, audioCtx.currentTime + attackTime + decayTime + sustainLevel * releaseTime);
+ }
+
+ function selectWaveform(event) {
+ const waveform = event.target.id;
+ document.querySelectorAll('.key').forEach(key => {
+ key.querySelector('oscillator').type = waveform;
+ });
+ }
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a3958f97e739/hermes3-8b.png b/data/artifacts/a3958f97e739/hermes3-8b.png
new file mode 100644
index 0000000..46a4e51
Binary files /dev/null and b/data/artifacts/a3958f97e739/hermes3-8b.png differ
diff --git a/data/artifacts/a3958f97e739/hf-qwen-coder-32b.html b/data/artifacts/a3958f97e739/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..95604c6
--- /dev/null
+++ b/data/artifacts/a3958f97e739/hf-qwen-coder-32b.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>One-Octave Piano</title>
+<style>
+ body { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; background-color: #f0f0f0; }
+ #piano { display: grid; grid-template-columns: repeat(7, 40px); gap: 2px; }
+ .key { width: 40px; height: 150px; background-color: white; border: 1px solid black; cursor: pointer; position: relative; }
+ .black-key { width: 24px; height: 90px; background-color: black; color: white; position: absolute; left: 18px; top: 0; z-index: 1; }
+ #controls { margin-top: 20px; }
+ select, button { padding: 5px; margin: 0 5px; }
+</style>
+</head>
+<body>
+<div id="piano">
+ <div class="key" data-note="C4"></div><div class="black-key">C#4</div>
+ <div class="key" data-note="D4"></div><div class="black-key">D#4</div>
+ <div class="key" data-note="E4"></div>
+ <div class="key" data-note="F4"></div><div class="black-key">F#4</div>
+ <div class="key" data-note="G4"></div><div class="black-key">G#4</div>
+ <div class="key" data-note="A4"></div><div class="black-key">A#4</div>
+ <div class="key" data-note="B4"></div>
+</div>
+<div id="controls">
+ <select id="waveform">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="sawtooth">Sawtooth</option>
+ </select>
+ <label><input type="checkbox" id="sustain"> Sustain</label>
+</div>
+
+<script>
+const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+let notes = {};
+['C4', 'C#4', 'D4', 'D#4', 'E4', 'F4', 'F#4', 'G4', 'G#4', 'A4', 'A#4', 'B4'].forEach(note => {
+ let freq = Math.pow(2, (parseInt(note[1]) + 1) * 12 - 69) * 440;
+ notes[note] = {osc: null, env: audioCtx.createGain(), freq};
+});
+
+document.querySelectorAll('.key').forEach(key => {
+ key.addEventListener('mousedown', playNote);
+ key.addEventListener('mouseup', releaseNote);
+ key.addEventListener('mouseenter', e => e.buttons && playNote(e));
+ key.addEventListener('mouseleave', e => !e.buttons && releaseNote(e));
+});
+
+function playNote(e) {
+ let note = e.target.dataset.note;
+ if (!notes[note].osc) {
+ notes[note].osc = audioCtx.createOscillator();
+ notes[note].osc.type = document.getElementById('waveform').value;
+ notes[note].osc.frequency.setValueAtTime(notes[note].freq, audioCtx.currentTime);
+ notes[note].osc.connect(notes[note].env).connect(audioCtx.destination);
+ notes[note].osc.start();
+ }
+ notes[note].env.gain.cancelScheduledValues(0);
+ notes[note].env.gain.setValueAtTime(1, audioCtx.currentTime);
+}
+
+function releaseNote(e) {
+ let note = e.target.dataset.note;
+ if (notes[note].osc) {
+ notes[note].env.gain.setTargetAtTime(document.getElementById('sustain').checked ? 1 : 0.001, audioCtx.currentTime, 0.2);
+ if (!document.getElementById('sustain').checked) {
+ setTimeout(() => {
+ notes[note].osc.stop();
+ notes[note].osc.disconnect();
+ notes[note].osc = null;
+ }, 200);
+ }
+ }
+}
+
+document.addEventListener('keydown', e => {
+ let noteMap = { 'a': 'C4', 'w': 'C#4', 's': 'D4', 'e': 'D#4', 'd': 'E4', 'f': 'F4', 't': 'F#4', 'g': 'G4', 'y': 'G#4', 'h': 'A4', 'u': 'A#4', 'j': 'B4' };
+ if (noteMap[e.key] && !notes[noteMap[e.key]].osc) {
+ playNote({ target: document.querySelector(`.key[data-note="${noteMap[e.key]}"]`) });
+ }
+});
+
+document.addEventListener('keyup', e => {
+ let noteMap = { 'a': 'C4', 'w': 'C#4', 's': 'D4', 'e': 'D#4', 'd': 'E4', 'f': 'F4', 't': 'F#4', 'g': 'G4', 'y': 'G#4', 'h': 'A4', 'u': 'A#4', 'j': 'B4' };
+ if (noteMap[e.key] && notes[noteMap[e.key]].osc) {
+ releaseNote({ target: document.querySelector(`.key[data-note="${noteMap[e.key]}"]`) });
+ }
+});
+
+document.getElementById('waveform').addEventListener('change', e => {
+ Object.values(notes).forEach(note => note.osc && (note.osc.type = e.target.value));
+});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a3958f97e739/hf-qwen-coder-32b.png b/data/artifacts/a3958f97e739/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..71724c3
Binary files /dev/null and b/data/artifacts/a3958f97e739/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/a3958f97e739/kimi.html b/data/artifacts/a3958f97e739/kimi.html
new file mode 100644
index 0000000..459ce24
--- /dev/null
+++ b/data/artifacts/a3958f97e739/kimi.html
@@ -0,0 +1,326 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>One Octave Piano</title>
+<style>
+ body {
+ font-family: Arial, sans-serif;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 100vh;
+ margin: 0;
+ background: #222;
+ color: #fff;
+ }
+
+ .controls {
+ margin-bottom: 20px;
+ display: flex;
+ gap: 20px;
+ align-items: center;
+ background: #333;
+ padding: 15px 25px;
+ border-radius: 10px;
+ }
+
+ .control-group {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+
+ select, input[type="checkbox"] {
+ cursor: pointer;
+ padding: 5px;
+ border-radius: 4px;
+ border: none;
+ }
+
+ .piano-container {
+ position: relative;
+ display: inline-flex;
+ background: #000;
+ padding: 10px;
+ border-radius: 8px;
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
+ }
+
+ .piano {
+ position: relative;
+ display: flex;
+ user-select: none;
+ }
+
+ .key {
+ cursor: pointer;
+ transition: background 0.05s;
+ }
+
+ .white {
+ width: 60px;
+ height: 200px;
+ background: linear-gradient(to bottom, #fff 0%, #eee 100%);
+ border: 1px solid #999;
+ border-radius: 0 0 5px 5px;
+ box-sizing: border-box;
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+ padding-bottom: 10px;
+ font-size: 12px;
+ color: #666;
+ position: relative;
+ z-index: 1;
+ }
+
+ .white:active, .white.active {
+ background: linear-gradient(to bottom, #ddd 0%, #ccc 100%);
+ box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
+ transform: translateY(2px);
+ }
+
+ .black {
+ width: 40px;
+ height: 120px;
+ background: linear-gradient(to bottom, #333 0%, #000 100%);
+ position: absolute;
+ z-index: 2;
+ border-radius: 0 0 3px 3px;
+ top: 0;
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+ padding-bottom: 8px;
+ font-size: 10px;
+ color: #aaa;
+ box-shadow: 0 4px 8px rgba(0,0,0,0.3);
+ }
+
+ .black:active, .black.active {
+ background: linear-gradient(to bottom, #444 0%, #222 100%);
+ box-shadow: inset 0 0 5px rgba(255,255,255,0.1), 0 2px 4px rgba(0,0,0,0.3);
+ height: 118px;
+ }
+
+ /* Position black keys */
+ .black[data-note="C#"] { left: 40px; }
+ .black[data-note="D#"] { left: 100px; }
+ .black[data-note="F#"] { left: 220px; }
+ .black[data-note="G#"] { left: 280px; }
+ .black[data-note="A#"] { left: 340px; }
+
+ .instructions {
+ margin-top: 20px;
+ color: #888;
+ font-size: 14px;
+ text-align: center;
+ }
+
+ kbd {
+ background: #444;
+ padding: 2px 6px;
+ border-radius: 3px;
+ font-family: monospace;
+ border: 1px solid #555;
+ }
+</style>
+</head>
+<body>
+
+<div class="controls">
+ <div class="control-group">
+ <label for="waveform">Waveform:</label>
+ <select id="waveform">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="sawtooth">Sawtooth</option>
+ </select>
+ </div>
+ <div class="control-group">
+ <label for="sustain">Sustain:</label>
+ <input type="checkbox" id="sustain">
+ </div>
+</div>
+
+<div class="piano-container">
+ <div class="piano" id="piano">
+ <!-- White keys -->
+ <div class="key white" data-note="C" id="key-C">A</div>
+ <div class="key white" data-note="D" id="key-D">S</div>
+ <div class="key white" data-note="E" id="key-E">D</div>
+ <div class="key white" data-note="F" id="key-F">F</div>
+ <div class="key white" data-note="G" id="key-G">G</div>
+ <div class="key white" data-note="A" id="key-A">H</div>
+ <div class="key white" data-note="B" id="key-B">J</div>
+
+ <!-- Black keys -->
+ <div class="key black" data-note="C#" id="key-C#">W</div>
+ <div class="key black" data-note="D#" id="key-D#">E</div>
+ <div class="key black" data-note="F#" id="key-F#">T</div>
+ <div class="key black" data-note="G#" id="key-G#">Y</div>
+ <div class="key black" data-note="A#" id="key-A#">U</div>
+ </div>
+</div>
+
+<div class="instructions">
+ Click keys or use keyboard: <kbd>A</kbd>-<kbd>J</kbd> for white keys, <kbd>W</kbd><kbd>E</kbd><kbd>T</kbd><kbd>Y</kbd><kbd>U</kbd> for black keys
+</div>
+
+<script>
+const AudioContext = window.AudioContext || window.webkitAudioContext;
+const audioContext = new AudioContext();
+const activeNotes = new Map();
+
+const noteFrequencies = {
+ 'C': 261.63,
+ 'C#': 277.18,
+ 'D': 293.66,
+ 'D#': 311.13,
+ 'E': 329.63,
+ 'F': 349.23,
+ 'F#': 369.99,
+ 'G': 392.00,
+ 'G#': 415.30,
+ 'A': 440.00,
+ 'A#': 466.16,
+ 'B': 493.88
+};
+
+const keyMap = {
+ 'a': 'C', 'w': 'C#',
+ 's': 'D', 'e': 'D#',
+ 'd': 'E',
+ 'f': 'F', 't': 'F#',
+ 'g': 'G', 'y': 'G#',
+ 'h': 'A', 'u': 'A#',
+ 'j': 'B'
+};
+
+const attackTime = 0.02;
+const decayTime = 0.1;
+const releaseTime = 0.3;
+
+function getSustainLevel() {
+ return document.getElementById('sustain').checked ? 0.6 : 0.0;
+}
+
+function playNote(note) {
+ if (activeNotes.has(note)) return;
+
+ if (audioContext.state === 'suspended') {
+ audioContext.resume();
+ }
+
+ const frequency = noteFrequencies[note];
+ const oscillator = audioContext.createOscillator();
+ const gainNode = audioContext.createGain();
+
+ const waveform = document.getElementById('waveform').value;
+ oscillator.type = waveform;
+ oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
+
+ oscillator.connect(gainNode);
+ gainNode.connect(audioContext.destination);
+
+ const now = audioContext.currentTime;
+ const sustainLevel = getSustainLevel();
+
+ // Attack
+ gainNode.gain.setValueAtTime(0, now);
+ gainNode.gain.linearRampToValueAtTime(1, now + attackTime);
+
+ // Decay to sustain
+ gainNode.gain.linearRampToValueAtTime(sustainLevel, now + attackTime + decayTime);
+
+ oscillator.start(now);
+
+ activeNotes.set(note, {
+ oscillator: oscillator,
+ gainNode: gainNode,
+ startTime: now
+ });
+
+ // Visual feedback
+ const keyElement = document.getElementById('key-' + note);
+ if (keyElement) keyElement.classList.add('active');
+}
+
+function stopNote(note) {
+ if (!activeNotes.has(note)) return;
+
+ const noteData = activeNotes.get(note);
+ const { oscillator, gainNode } = noteData;
+ const now = audioContext.currentTime;
+
+ // Cancel scheduled values and release
+ gainNode.gain.cancelScheduledValues(now);
+ gainNode.gain.setValueAtTime(gainNode.gain.value, now);
+ gainNode.gain.exponentialRampToValueAtTime(0.001, now + releaseTime);
+
+ oscillator.stop(now + releaseTime);
+
+ activeNotes.delete(note);
+
+ // Remove visual feedback
+ const keyElement = document.getElementById('key-' + note);
+ if (keyElement) keyElement.classList.remove('active');
+}
+
+// Keyboard events
+window.addEventListener('keydown', (e) => {
+ if (e.repeat) return;
+ const key = e.key.toLowerCase();
+ if (keyMap[key]) {
+ playNote(keyMap[key]);
+ }
+});
+
+window.addEventListener('keyup', (e) => {
+ const key = e.key.toLowerCase();
+ if (keyMap[key]) {
+ stopNote(keyMap[key]);
+ }
+});
+
+// Mouse/Touch events
+document.querySelectorAll('.key').forEach(key => {
+ const note = key.dataset.note;
+
+ // Mouse
+ key.addEventListener('mousedown', (e) => {
+ e.preventDefault();
+ playNote(note);
+ });
+
+ key.addEventListener('mouseup', () => {
+ stopNote(note);
+ });
+
+ key.addEventListener('mouseleave', () => {
+ if (activeNotes.has(note)) {
+ stopNote(note);
+ }
+ });
+
+ // Touch
+ key.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ playNote(note);
+ });
+
+ key.addEventListener('touchend', (e) => {
+ e.preventDefault();
+ stopNote(note);
+ });
+});
+
+// Prevent context menu on right click
+window.addEventListener('contextmenu', e => e.preventDefault());
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a3958f97e739/kimi.png b/data/artifacts/a3958f97e739/kimi.png
new file mode 100644
index 0000000..08f72ad
Binary files /dev/null and b/data/artifacts/a3958f97e739/kimi.png differ
diff --git a/data/artifacts/a3958f97e739/qwen25-7b.html b/data/artifacts/a3958f97e739/qwen25-7b.html
new file mode 100644
index 0000000..c6a9d91
--- /dev/null
+++ b/data/artifacts/a3958f97e739/qwen25-7b.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>One-Octave Piano</title>
+ <style>
+ body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; }
+ .piano { width: 350px; border: 2px solid #000; padding: 10px; background-color: #f0f0f0; }
+ .key { width: 40px; height: 160px; float: left; margin-right: -5px; cursor: pointer; transition: transform 0.1s, background-color 0.1s; }
+ .key:hover { transform: scale(1.02); background-color: #ddd; }
+ </style>
+</head>
+<body>
+<div class="piano">
+ <div id="c" class="key"></div>
+ <div id="cs" class="key"></div>
+ <div id="d" class="key"></div>
+ <div id="ds" class="key"></div>
+ <div id="e" class="key"></div>
+ <div id="f" class="key"></div>
+ <div id="fs" class="key"></div>
+ <div id="g" class="key"></div>
+ <div id="gs" class="key"></div>
+ <div id="a" class="key"></div>
+ <div id="as" class="key"></div>
+ <div id="b" class="key"></div>
+</div>
+
+<script>
+const keys = document.querySelectorAll('.key');
+const notes = ['c', 'cs', 'd', 'ds', 'e', 'f', 'fs', 'g', 'gs', 'a', 'as', 'b'];
+const oscSine = new AudioContext().createOscillator();
+oscSine.type = 'sine';
+const oscSquare = new AudioContext().createOscillator();
+oscSquare.type = 'square';
+const oscSaw = new AudioContext().createOscillator();
+oscSaw.type = 'sawtooth';
+const envelope = new AudioContext().createGain();
+envelope.gain.setValueAtTime(0, 0);
+const releaseTime = 1;
+let sustain = true;
+
+function playNote(id) {
+ const key = document.getElementById(id);
+ if (!key.dataset.playing) {
+ key.dataset.playing = 'true';
+ key.style.background = '#ccc';
+ const freq = notes.indexOf(id) * 220;
+ oscSine.frequency.setValueAtTime(freq, AudioContext.currentTime);
+ oscSquare.frequency.setValueAtTime(freq, AudioContext.currentTime);
+ oscSaw.frequency.setValueAtTime(freq, AudioContext.currentTime);
+ envelope.gain.setValueAtTime(1, AudioContext.currentTime);
+ const release = () => {
+ if (sustain) key.style.background = '#ddd';
+ key.dataset.playing = null;
+ envelope.gain.cancelScheduledValues(AudioContext.currentTime);
+ envelope.gain.setValueAtTime(0, AudioContext.currentTime + 0.5);
+ oscSine.stop(AudioContext.currentTime + releaseTime);
+ oscSquare.stop(AudioContext.currentTime + releaseTime);
+ oscSaw.stop(AudioContext.currentTime + releaseTime);
+ };
+ setTimeout(release, 100);
+ }
+}
+
+keys.forEach(key => {
+ key.addEventListener('click', () => playNote(key.id));
+ window.addEventListener('keydown', e => {
+ if (e.key === key.id) playNote(key.id);
+ });
+});
+
+function toggleSustain() { sustain = !sustain; }
+
+document.querySelector('.piano').addEventListener('mousedown', event => {
+ const key = event.target;
+ if (!key.dataset.playing && notes.includes(key.id)) playNote(key.id);
+});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a3958f97e739/qwen25-7b.png b/data/artifacts/a3958f97e739/qwen25-7b.png
new file mode 100644
index 0000000..b98475e
Binary files /dev/null and b/data/artifacts/a3958f97e739/qwen25-7b.png differ
diff --git a/data/artifacts/a3958f97e739/qwen3-14b.html b/data/artifacts/a3958f97e739/qwen3-14b.html
new file mode 100644
index 0000000..2062cd6
--- /dev/null
+++ b/data/artifacts/a3958f97e739/qwen3-14b.html
@@ -0,0 +1,213 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>One-Octave Piano</title>
+ <style>
+ body {
+ margin: 0;
+ background: #111;
+ color: #fff;
+ font-family: sans-serif;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ overflow: hidden;
+ }
+ #piano {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ width: 80%;
+ margin-bottom: 20px;
+ }
+ .key {
+ width: 50px;
+ height: 200px;
+ background: #333;
+ border: 1px solid #555;
+ position: relative;
+ cursor: pointer;
+ transition: background 0.1s;
+ }
+ .key:active {
+ background: #555;
+ }
+ .key.white {
+ background: #fff;
+ border: 1px solid #000;
+ color: #000;
+ box-shadow: inset 0 0 0 2px #000;
+ }
+ .key.white:active {
+ background: #ddd;
+ }
+ .key.white.active {
+ background: #bbb;
+ }
+ .key.black {
+ background: #000;
+ color: #fff;
+ position: absolute;
+ width: 30px;
+ height: 120px;
+ top: -60px;
+ left: 20px;
+ z-index: 1;
+ }
+ .key.black:active {
+ background: #222;
+ }
+ .key.black.active {
+ background: #444;
+ }
+ .controls {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 10px;
+ }
+ .controls label {
+ display: inline-block;
+ margin: 5px;
+ }
+ .controls select, .controls input {
+ padding: 5px;
+ font-size: 16px;
+ }
+ </style>
+</head>
+<body>
+ <div id="piano"></div>
+ <div class="controls">
+ <label>Waveform:
+ <select id="waveform">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="sawtooth">Sawtooth</option>
+ </select>
+ </label>
+ <label>
+ <input type="checkbox" id="sustain"> Sustain
+ </label>
+ </div>
+
+ <script>
+ const piano = document.getElementById('piano');
+ const keys = [];
+ const noteFrequencies = {
+ 'C': 261.63,
+ 'C#': 277.18,
+ 'D': 293.66,
+ 'D#': 311.13,
+ 'E': 329.63,
+ 'F': 349.23,
+ 'F#': 369.99,
+ 'G': 392.00,
+ 'G#': 415.30,
+ 'A': 440.00,
+ 'A#': 466.16,
+ 'B': 493.88
+ };
+
+ const whiteKeys = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
+ const blackKeys = ['C#', 'D#', 'F#', 'G#', 'A#'];
+
+ // Generate the piano keys
+ for (let i = 0; i < 12; i++) {
+ const note = Object.keys(noteFrequencies)[i];
+ const key = document.createElement('div');
+ key.className = 'key';
+ key.dataset.note = note;
+ key.dataset.freq = noteFrequencies[note];
+
+ if (whiteKeys.includes(note)) {
+ key.classList.add('white');
+ key.textContent = note;
+ } else {
+ key.classList.add('black');
+ key.textContent = note;
+ }
+
+ key.addEventListener('mousedown', (e) => {
+ playNote(e.currentTarget);
+ e.currentTarget.classList.add('active');
+ });
+ key.addEventListener('mouseup', (e) => {
+ stopNote(e.currentTarget);
+ e.currentTarget.classList.remove('active');
+ });
+ key.addEventListener('mouseleave', (e) => {
+ stopNote(e.currentTarget);
+ e.currentTarget.classList.remove('active');
+ });
+
+ piano.appendChild(key);
+ keys.push(key);
+ }
+
+ document.addEventListener('keydown', (e) => {
+ const key = keys.find(k => k.dataset.note === e.key);
+ if (key) {
+ key.classList.add('active');
+ playNote(key);
+ }
+ });
+
+ document.addEventListener('keyup', (e) => {
+ const key = keys.find(k => k.dataset.note === e.key);
+ if (key) {
+ key.classList.remove('active');
+ stopNote(key);
+ }
+ });
+
+ const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ let oscillators = {};
+ let envelopeGain = audioCtx.createGain();
+ envelopeGain.connect(audioCtx.destination);
+
+ const waveformSelect = document.getElementById('waveform');
+ const sustainToggle = document.getElementById('sustain');
+
+ waveformSelect.addEventListener('change', (e) => {
+ for (let osc of Object.values(oscillators)) {
+ osc.type = e.target.value;
+ }
+ });
+
+ sustainToggle.addEventListener('change', (e) => {
+ if (e.target.checked) {
+ envelopeGain.gain.value = 0.1;
+ } else {
+ envelopeGain.gain.value = 0;
+ }
+ });
+
+ function playNote(key) {
+ const freq = parseFloat(key.dataset.freq);
+ if (!oscillators[key.dataset.note]) {
+ oscillators[key.dataset.note] = audioCtx.createOscillator();
+ oscillators[key.dataset.note].type = waveformSelect.value;
+ oscillators[key.dataset.note].frequency.value = freq;
+ oscillators[key.dataset.note].connect(envelopeGain);
+ oscillators[key.dataset.note].start();
+ }
+ envelopeGain.gain.setValueAtTime(0, audioCtx.currentTime);
+ envelopeGain.gain.linearRampToValueAtTime(0.1, audioCtx.currentTime + 0.01);
+ envelopeGain.gain.linearRampToValueAtTime(0.1, audioCtx.currentTime + 0.1);
+ envelopeGain.gain.linearRampToValueAtTime(0.1, audioCtx.currentTime + 0.4);
+ envelopeGain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.5);
+ }
+
+ function stopNote(key) {
+ if (oscillators[key.dataset.note]) {
+ envelopeGain.gain.setValueAtTime(envelopeGain.gain.value, audioCtx.currentTime);
+ envelopeGain.gain.exponentialRampToValueAtTime(0.0001, audioCtx.currentTime + 0.1);
+ }
+ }
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a3958f97e739/qwen3-14b.png b/data/artifacts/a3958f97e739/qwen3-14b.png
new file mode 100644
index 0000000..9910619
Binary files /dev/null and b/data/artifacts/a3958f97e739/qwen3-14b.png differ
diff --git a/data/artifacts/b14c4e0ccc4e/claude-code.html b/data/artifacts/b14c4e0ccc4e/claude-code.html
new file mode 100644
index 0000000..8b4535f
--- /dev/null
+++ b/data/artifacts/b14c4e0ccc4e/claude-code.html
@@ -0,0 +1,474 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Weather Card</title>
+<style>
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+
+ html, body { height: 100%; overflow: hidden; }
+
+ body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ background: #1a2238;
+ }
+
+ .bg {
+ position: fixed;
+ inset: 0;
+ opacity: 0;
+ transition: opacity 1.2s ease;
+ z-index: 0;
+ }
+ .bg.visible { opacity: 1; }
+ #bg-sunny { background: linear-gradient(160deg, #ff9a3d 0%, #ffc55c 35%, #7ec8e3 100%); }
+ #bg-cloudy { background: linear-gradient(160deg, #5b6a82 0%, #8d9bb0 55%, #b9c4d1 100%); }
+ #bg-rainy { background: linear-gradient(160deg, #2c3e50 0%, #3f5872 55%, #56708c 100%); }
+ #bg-snowy { background: linear-gradient(160deg, #6b7f9e 0%, #a8bdd4 55%, #dfe9f2 100%); }
+
+ #sky {
+ position: fixed;
+ inset: 0;
+ z-index: 1;
+ }
+
+ .card {
+ position: relative;
+ z-index: 2;
+ width: min(360px, 88vw);
+ padding: 42px 36px 34px;
+ border-radius: 28px;
+ background: rgba(255, 255, 255, 0.16);
+ backdrop-filter: blur(18px) saturate(150%);
+ -webkit-backdrop-filter: blur(18px) saturate(150%);
+ border: 1px solid rgba(255, 255, 255, 0.35);
+ border-bottom-color: rgba(255, 255, 255, 0.12);
+ border-right-color: rgba(255, 255, 255, 0.12);
+ box-shadow: 0 24px 60px rgba(10, 20, 40, 0.35), inset 0 1px 0 rgba(255,255,255,0.4);
+ color: #fff;
+ text-align: center;
+ overflow: hidden;
+ }
+
+ .card::before {
+ content: "";
+ position: absolute;
+ top: -60%;
+ left: -30%;
+ width: 90%;
+ height: 120%;
+ background: linear-gradient(115deg, rgba(255,255,255,0.28), rgba(255,255,255,0) 60%);
+ transform: rotate(8deg);
+ pointer-events: none;
+ }
+
+ .content { position: relative; transition: opacity .35s ease, transform .35s ease; }
+ .content.swap { opacity: 0; transform: translateY(8px); }
+
+ .city {
+ font-size: 1.35rem;
+ font-weight: 600;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ text-shadow: 0 2px 10px rgba(0,0,0,0.25);
+ }
+
+ .date {
+ margin-top: 4px;
+ font-size: 0.8rem;
+ font-weight: 400;
+ opacity: 0.85;
+ letter-spacing: 0.08em;
+ }
+
+ .icon-wrap {
+ height: 110px;
+ margin: 18px 0 6px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ #icon { width: 110px; height: 110px; display: block; }
+
+ .temp {
+ font-size: 4.6rem;
+ font-weight: 200;
+ line-height: 1;
+ letter-spacing: -0.02em;
+ text-shadow: 0 4px 18px rgba(0,0,0,0.25);
+ }
+ .temp sup { font-size: 1.8rem; font-weight: 300; vertical-align: super; }
+
+ .condition {
+ margin-top: 10px;
+ font-size: 1.05rem;
+ font-weight: 500;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+ opacity: 0.95;
+ }
+
+ .meta {
+ display: flex;
+ justify-content: center;
+ gap: 26px;
+ margin-top: 22px;
+ padding-top: 18px;
+ border-top: 1px solid rgba(255,255,255,0.25);
+ font-size: 0.8rem;
+ opacity: 0.9;
+ }
+ .meta div span { display: block; font-size: 0.68rem; opacity: 0.75; letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 3px; }
+ .meta div b { font-weight: 600; font-size: 0.95rem; }
+
+ #cycle {
+ position: relative;
+ margin-top: 26px;
+ padding: 13px 30px;
+ font-size: 0.85rem;
+ font-weight: 600;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: #fff;
+ background: rgba(255, 255, 255, 0.22);
+ border: 1px solid rgba(255, 255, 255, 0.45);
+ border-radius: 999px;
+ cursor: pointer;
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ transition: background .25s ease, transform .15s ease, box-shadow .25s ease;
+ box-shadow: 0 6px 20px rgba(10,20,40,0.2);
+ }
+ #cycle:hover { background: rgba(255,255,255,0.34); box-shadow: 0 8px 26px rgba(10,20,40,0.3); }
+ #cycle:active { transform: scale(0.96); }
+</style>
+</head>
+<body>
+
+<div class="bg visible" id="bg-sunny"></div>
+<div class="bg" id="bg-cloudy"></div>
+<div class="bg" id="bg-rainy"></div>
+<div class="bg" id="bg-snowy"></div>
+
+<canvas id="sky"></canvas>
+
+<div class="card">
+ <div class="content" id="content">
+ <div class="city" id="city">Los Angeles</div>
+ <div class="date" id="date"></div>
+ <div class="icon-wrap"><canvas id="icon" width="220" height="220"></canvas></div>
+ <div class="temp"><span id="temp">84</span><sup>°F</sup></div>
+ <div class="condition" id="cond">Sunny</div>
+ <div class="meta">
+ <div><span>Humidity</span><b id="hum">22%</b></div>
+ <div><span>Wind</span><b id="wind">6 mph</b></div>
+ <div><span>Feels</span><b id="feels">86°</b></div>
+ </div>
+ </div>
+ <button id="cycle">Next Weather</button>
+</div>
+
+<script>
+(function () {
+ "use strict";
+
+ var STATES = [
+ { key: "sunny", city: "Los Angeles", temp: 84, cond: "Sunny", hum: "22%", wind: "6 mph", feels: "86°" },
+ { key: "cloudy", city: "London", temp: 61, cond: "Cloudy", hum: "68%", wind: "12 mph", feels: "59°" },
+ { key: "rainy", city: "Seattle", temp: 54, cond: "Rain", hum: "91%", wind: "15 mph", feels: "50°" },
+ { key: "snowy", city: "Oslo", temp: 27, cond: "Snow", hum: "84%", wind: "9 mph", feels: "19°" }
+ ];
+ var idx = 0;
+
+ document.getElementById("date").textContent =
+ new Date().toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric" });
+
+ /* ---------- sky canvas ---------- */
+ var sky = document.getElementById("sky");
+ var ctx = sky.getContext("2d");
+ var W = 0, H = 0, DPR = Math.min(window.devicePixelRatio || 1, 2);
+
+ function resize() {
+ W = window.innerWidth; H = window.innerHeight;
+ sky.width = W * DPR; sky.height = H * DPR;
+ sky.style.width = W + "px"; sky.style.height = H + "px";
+ ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
+ }
+ window.addEventListener("resize", resize);
+ resize();
+
+ /* crossfaded intensity per system */
+ var weights = { sun: 1, cloud: 0.15, rain: 0, snow: 0 };
+ var TARGETS = {
+ sunny: { sun: 1, cloud: 0.15, rain: 0, snow: 0 },
+ cloudy: { sun: 0, cloud: 1, rain: 0, snow: 0 },
+ rainy: { sun: 0, cloud: 0.85, rain: 1, snow: 0 },
+ snowy: { sun: 0, cloud: 0.45, rain: 0, snow: 1 }
+ };
+
+ /* clouds */
+ var clouds = [];
+ for (var i = 0; i < 7; i++) {
+ clouds.push({
+ x: Math.random(), y: 0.06 + Math.random() * 0.32,
+ s: 0.7 + Math.random() * 0.9,
+ v: 0.008 + Math.random() * 0.014,
+ puffs: 4 + Math.floor(Math.random() * 3),
+ seed: Math.random() * 10
+ });
+ }
+
+ /* rain */
+ var drops = [];
+ for (i = 0; i < 160; i++) {
+ drops.push({ x: Math.random(), y: Math.random(), l: 10 + Math.random() * 14, v: 0.9 + Math.random() * 0.8 });
+ }
+
+ /* snow */
+ var flakes = [];
+ for (i = 0; i < 110; i++) {
+ flakes.push({ x: Math.random(), y: Math.random(), r: 1.2 + Math.random() * 2.6, v: 0.08 + Math.random() * 0.14, p: Math.random() * Math.PI * 2 });
+ }
+
+ function drawSun(t, a) {
+ if (a <= 0.01) return;
+ var cx = W * 0.5, cy = H * 0.24, R = Math.min(W, H) * 0.09;
+ ctx.save();
+ ctx.globalAlpha = a;
+
+ var glow = ctx.createRadialGradient(cx, cy, R * 0.2, cx, cy, R * 4.2);
+ glow.addColorStop(0, "rgba(255,235,170,0.85)");
+ glow.addColorStop(0.4, "rgba(255,210,120,0.25)");
+ glow.addColorStop(1, "rgba(255,210,120,0)");
+ ctx.fillStyle = glow;
+ ctx.fillRect(cx - R * 4.5, cy - R * 4.5, R * 9, R * 9);
+
+ ctx.translate(cx, cy);
+ ctx.rotate(t * 0.00012);
+ ctx.strokeStyle = "rgba(255,240,190,0.7)";
+ ctx.lineWidth = Math.max(3, R * 0.12);
+ ctx.lineCap = "round";
+ for (var k = 0; k < 12; k++) {
+ var pulse = 1 + 0.08 * Math.sin(t * 0.002 + k);
+ ctx.beginPath();
+ ctx.moveTo(R * 1.35, 0);
+ ctx.lineTo(R * 1.75 * pulse, 0);
+ ctx.stroke();
+ ctx.rotate(Math.PI / 6);
+ }
+ var body = ctx.createRadialGradient(-R * 0.3, -R * 0.3, R * 0.1, 0, 0, R);
+ body.addColorStop(0, "#fff6d8");
+ body.addColorStop(1, "#ffd257");
+ ctx.fillStyle = body;
+ ctx.beginPath();
+ ctx.arc(0, 0, R, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ function drawCloud(c, t, a) {
+ var x = ((c.x + t * 0.00001 * c.v * 60) % 1.3) - 0.15;
+ var cx = x * W, cy = c.y * H + Math.sin(t * 0.0004 + c.seed) * 6;
+ var base = Math.min(W, H) * 0.075 * c.s;
+ ctx.save();
+ ctx.globalAlpha = a * 0.85;
+ ctx.fillStyle = "rgba(255,255,255,0.9)";
+ for (var p = 0; p < c.puffs; p++) {
+ var px = cx + (p - (c.puffs - 1) / 2) * base * 0.9;
+ var pr = base * (0.75 + 0.5 * Math.sin(c.seed + p * 2.3) * 0.5 + 0.35);
+ var py = cy - Math.abs(Math.sin(c.seed + p)) * base * 0.45;
+ ctx.beginPath();
+ ctx.arc(px, py, Math.abs(pr), 0, Math.PI * 2);
+ ctx.fill();
+ }
+ ctx.globalAlpha = a * 0.5;
+ ctx.fillStyle = "rgba(190,205,225,0.8)";
+ ctx.beginPath();
+ ctx.ellipse(cx, cy + base * 0.35, base * c.puffs * 0.52, base * 0.42, 0, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ function drawRain(dt, a) {
+ if (a <= 0.01) return;
+ ctx.save();
+ ctx.globalAlpha = a * 0.7;
+ ctx.strokeStyle = "rgba(200,225,255,0.75)";
+ ctx.lineWidth = 1.4;
+ ctx.lineCap = "round";
+ var slant = 0.18;
+ for (var i = 0; i < drops.length; i++) {
+ var d = drops[i];
+ d.y += d.v * dt * 0.0011;
+ d.x += d.v * dt * 0.0011 * slant;
+ if (d.y > 1.05) { d.y = -0.05; d.x = Math.random(); }
+ var x = d.x * W, y = d.y * H;
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x - d.l * slant, y - d.l);
+ ctx.stroke();
+ }
+ ctx.restore();
+ }
+
+ function drawSnow(dt, t, a) {
+ if (a <= 0.01) return;
+ ctx.save();
+ ctx.globalAlpha = a * 0.9;
+ ctx.fillStyle = "rgba(255,255,255,0.95)";
+ for (var i = 0; i < flakes.length; i++) {
+ var f = flakes[i];
+ f.y += f.v * dt * 0.0011;
+ f.p += dt * 0.0012;
+ if (f.y > 1.03) { f.y = -0.03; f.x = Math.random(); }
+ var x = (f.x + Math.sin(f.p) * 0.012) * W;
+ ctx.beginPath();
+ ctx.arc(x, f.y * H, f.r, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ ctx.restore();
+ }
+
+ /* ---------- icon canvas (inside card) ---------- */
+ var icon = document.getElementById("icon");
+ var ictx = icon.getContext("2d");
+ var IS = 220;
+
+ function iconSun(t) {
+ var c = IS / 2, R = 38;
+ ictx.save();
+ ictx.translate(c, c);
+ ictx.rotate(t * 0.0004);
+ ictx.strokeStyle = "rgba(255,244,200,0.95)";
+ ictx.lineWidth = 7;
+ ictx.lineCap = "round";
+ for (var k = 0; k < 8; k++) {
+ var pulse = 1 + 0.1 * Math.sin(t * 0.003 + k * 1.5);
+ ictx.beginPath();
+ ictx.moveTo(R + 14, 0);
+ ictx.lineTo((R + 26) * pulse, 0);
+ ictx.stroke();
+ ictx.rotate(Math.PI / 4);
+ }
+ var g = ictx.createRadialGradient(-12, -12, 4, 0, 0, R);
+ g.addColorStop(0, "#fffbe8");
+ g.addColorStop(1, "#ffd257");
+ ictx.fillStyle = g;
+ ictx.beginPath();
+ ictx.arc(0, 0, R, 0, Math.PI * 2);
+ ictx.fill();
+ ictx.restore();
+ }
+
+ function iconCloud(t, dx, dy, scale, alpha) {
+ ictx.save();
+ ictx.globalAlpha = alpha;
+ ictx.translate(IS / 2 + dx + Math.sin(t * 0.001) * 5, IS / 2 + dy);
+ ictx.scale(scale, scale);
+ ictx.fillStyle = "#ffffff";
+ ictx.beginPath();
+ ictx.arc(-30, 8, 24, 0, Math.PI * 2);
+ ictx.arc(-2, -8, 30, 0, Math.PI * 2);
+ ictx.arc(28, 8, 22, 0, Math.PI * 2);
+ ictx.fill();
+ ictx.fillRect(-30, 6, 58, 24);
+ ictx.beginPath();
+ ictx.ellipse(0, 30, 52, 12, 0, 0, Math.PI * 2);
+ ictx.fill();
+ ictx.restore();
+ }
+
+ function iconRain(t) {
+ iconCloud(t, 0, -18, 1, 1);
+ ictx.save();
+ ictx.strokeStyle = "rgba(190,225,255,0.95)";
+ ictx.lineWidth = 4;
+ ictx.lineCap = "round";
+ for (var i = 0; i < 5; i++) {
+ var phase = ((t * 0.0016 + i * 0.23) % 1);
+ var x = IS / 2 - 40 + i * 20;
+ var y = IS / 2 + 26 + phase * 40;
+ ictx.globalAlpha = 1 - phase;
+ ictx.beginPath();
+ ictx.moveTo(x, y);
+ ictx.lineTo(x - 3, y - 12);
+ ictx.stroke();
+ }
+ ictx.restore();
+ }
+
+ function iconSnow(t) {
+ iconCloud(t, 0, -18, 1, 1);
+ ictx.save();
+ ictx.fillStyle = "#ffffff";
+ for (var i = 0; i < 5; i++) {
+ var phase = ((t * 0.0008 + i * 0.31) % 1);
+ var x = IS / 2 - 42 + i * 21 + Math.sin(t * 0.003 + i) * 5;
+ var y = IS / 2 + 24 + phase * 44;
+ ictx.globalAlpha = 1 - phase;
+ ictx.beginPath();
+ ictx.arc(x, y, 4.5, 0, Math.PI * 2);
+ ictx.fill();
+ }
+ ictx.restore();
+ }
+
+ /* ---------- main loop ---------- */
+ var last = 0;
+ function frame(t) {
+ var dt = last ? Math.min(t - last, 50) : 16;
+ last = t;
+
+ var target = TARGETS[STATES[idx].key];
+ var ease = 1 - Math.pow(0.997, dt);
+ for (var k in weights) weights[k] += (target[k] - weights[k]) * ease;
+
+ ctx.clearRect(0, 0, W, H);
+ drawSun(t, weights.sun);
+ for (var i = 0; i < clouds.length; i++) {
+ var wc = i < 2 ? Math.max(weights.cloud, weights.sun * 0.15) : weights.cloud;
+ drawCloud(clouds[i], t, wc);
+ }
+ drawRain(dt, weights.rain);
+ drawSnow(dt, t, weights.snow);
+
+ ictx.clearRect(0, 0, IS, IS);
+ var key = STATES[idx].key;
+ if (key === "sunny") iconSun(t);
+ else if (key === "cloudy") { iconCloud(t, 10, 4, 0.7, 0.55); iconCloud(t, -6, -6, 1, 1); }
+ else if (key === "rainy") iconRain(t);
+ else iconSnow(t);
+
+ requestAnimationFrame(frame);
+ }
+ requestAnimationFrame(frame);
+
+ /* ---------- state cycling ---------- */
+ var content = document.getElementById("content");
+ document.getElementById("cycle").addEventListener("click", function () {
+ idx = (idx + 1) % STATES.length;
+ var s = STATES[idx];
+
+ document.querySelectorAll(".bg").forEach(function (el) { el.classList.remove("visible"); });
+ document.getElementById("bg-" + s.key).classList.add("visible");
+
+ content.classList.add("swap");
+ setTimeout(function () {
+ document.getElementById("city").textContent = s.city;
+ document.getElementById("temp").textContent = s.temp;
+ document.getElementById("cond").textContent = s.cond;
+ document.getElementById("hum").textContent = s.hum;
+ document.getElementById("wind").textContent = s.wind;
+ document.getElementById("feels").textContent = s.feels;
+ content.classList.remove("swap");
+ }, 350);
+ });
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/b14c4e0ccc4e/claude-code.png b/data/artifacts/b14c4e0ccc4e/claude-code.png
new file mode 100644
index 0000000..b1ad60e
Binary files /dev/null and b/data/artifacts/b14c4e0ccc4e/claude-code.png differ
diff --git a/data/artifacts/b14c4e0ccc4e/gemma3-12b.html b/data/artifacts/b14c4e0ccc4e/gemma3-12b.html
new file mode 100644
index 0000000..a15baeb
--- /dev/null
+++ b/data/artifacts/b14c4e0ccc4e/gemma3-12b.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Animated Weather Card</title>
+ <style>
+ body {
+ background: linear-gradient(to bottom, rgba(255,255,255,0.7), transparent);
+ font-family: sans-serif;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+ margin: 0;
+ }
+
+ .card {
+ background: rgba(255, 255, 255, 0.2); /* Glassmorphism */
+ border-radius: 20px;
+ padding: 30px;
+ width: 300px;
+ text-align: center;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ }
+
+ #weatherCanvas {
+ border-radius: 15px;
+ background: #87CEEB; /* Light Blue */
+ }
+
+ button {
+ margin-top: 20px;
+ padding: 10px 20px;
+ background: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.2)); /* Glassmorphism */
+ color: white;
+ border: none;
+ border-radius: 10px;
+ cursor: pointer;
+ }
+
+ </style>
+</head>
+<body>
+
+ <div class="card">
+ <h2>City: London</h2>
+ <p>Temperature: 25°C</p>
+ <canvas id="weatherCanvas" width="200" height="100"></canvas>
+ <button id="weatherButton">Next Weather</button>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('weatherCanvas');
+ const ctx = canvas.getContext('2d');
+ const weatherButton = document.getElementById('weatherButton');
+ let currentWeatherIndex = 0;
+ const weatherStates = [
+ { name: 'Sunny', animation: drawSun },
+ { name: 'Cloudy', animation: drawClouds },
+ { name: 'Rainy', animation: drawRain },
+ { name: 'Snowy', animation: drawSnow }
+ ];
+
+ function drawSun() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ const numRays = 12;
+ const radius = 50;
+ for (let i = 0; i < numRays; i++) {
+ const angle = (i / numRays) * Math.PI * 2;
+ ctx.beginPath();
+ ctx.moveTo(canvas.width / 2, canvas.height / 2 - radius);
+ ctx.lineTo(canvas.width / 2 + radius * Math.cos(angle), canvas.height / 2 - radius*0.5 );
+ ctx.strokeStyle = 'yellow';
+ ctx.lineWidth = 3;
+ ctx.stroke();
+ }
+
+ }
+
+ function drawClouds() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.beginPath();
+ ctx.arc(50, 40, 20, 0, Math.PI * 2);
+ ctx.fillStyle = 'white';
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(100, 30, 25, 0, Math.PI * 2);
+ ctx.fillStyle = 'white';
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(150, 45, 22, 0, Math.PI * 2);
+ ctx.fillStyle = 'white';
+ ctx.fill();
+ }
+
+ function drawRain() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for (let i = 0; i < 10; i++) {
+ ctx.beginPath();
+ ctx.moveTo(Math.random() * canvas.width, Math.random() * 5);
+ ctx.lineTo(Math.random() * canvas.width, canvas.height + Math.random()*5 );
+ ctx.strokeStyle = 'blue';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+ }
+
+ }
+
+ function drawSnow() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for (let i = 0; i < 10; i++) {
+ ctx.beginPath();
+ ctx.moveTo(Math.random() * canvas.width, Math.random() * 5);
+ ctx.lineTo(Math.random() * canvas.width, canvas.height + Math.random()*5 );
+ ctx.strokeStyle = 'white';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+ }
+ }
+
+
+ weatherButton.addEventListener('click', () => {
+ currentWeatherIndex = (currentWeatherIndex + 1) % weatherStates.length;
+ weatherStates[currentWeatherIndex].animation();
+ });
+
+ weatherStates[0].animation(); // Initial animation
+ </script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/b14c4e0ccc4e/gemma3-12b.png b/data/artifacts/b14c4e0ccc4e/gemma3-12b.png
new file mode 100644
index 0000000..fa72b32
Binary files /dev/null and b/data/artifacts/b14c4e0ccc4e/gemma3-12b.png differ
diff --git a/data/artifacts/b14c4e0ccc4e/gpt.html b/data/artifacts/b14c4e0ccc4e/gpt.html
new file mode 100644
index 0000000..b92a9d2
--- /dev/null
+++ b/data/artifacts/b14c4e0ccc4e/gpt.html
@@ -0,0 +1,953 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>Animated Weather Card</title>
+ <style>
+ :root {
+ --glass-bg: rgba(255, 255, 255, 0.16);
+ --glass-border: rgba(255, 255, 255, 0.35);
+ --text-main: #ffffff;
+ --accent: #64b5ff;
+ --shadow-soft: 0 20px 40px rgba(0, 0, 0, 0.45);
+ --blur-amount: 18px;
+ --transition-fast: 0.55s cubic-bezier(0.25, 0.8, 0.25, 1);
+ --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text",
+ "Segoe UI", sans-serif;
+ }
+
+ * {
+ box-sizing: border-box;
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ html,
+ body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ }
+
+ body {
+ font-family: var(--font-sans);
+ color: var(--text-main);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: radial-gradient(circle at 10% 0%, #0b1020 0%, #050611 40%, #010104 100%);
+ overflow: hidden;
+ }
+
+ /* Background gradient that shifts with weather */
+ .bg-orbit {
+ position: fixed;
+ inset: -40vmax;
+ background: radial-gradient(circle at 0% 0%, #3f84ff 0%, transparent 45%),
+ radial-gradient(circle at 100% 0%, #ff8a00 0%, transparent 50%),
+ radial-gradient(circle at 0% 100%, #673ab7 0%, transparent 50%),
+ radial-gradient(circle at 100% 100%, #00bcd4 0%, transparent 55%);
+ filter: blur(40px);
+ opacity: 0.75;
+ transform-origin: center;
+ animation: bg-orbit 26s linear infinite alternate;
+ pointer-events: none;
+ }
+
+ @keyframes bg-orbit {
+ 0% {
+ transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
+ }
+ 100% {
+ transform: translate3d(4vmin, -2vmin, 0) rotate(10deg) scale(1.05);
+ }
+ }
+
+ .bg-noise {
+ position: fixed;
+ inset: -40px;
+ background-image: repeating-linear-gradient(
+ 90deg,
+ rgba(255, 255, 255, 0.02) 0,
+ rgba(255, 255, 255, 0.02) 1px,
+ transparent 1px,
+ transparent 2px
+ ),
+ repeating-linear-gradient(
+ 0deg,
+ rgba(255, 255, 255, 0.018) 0,
+ rgba(255, 255, 255, 0.018) 1px,
+ transparent 1px,
+ transparent 2px
+ );
+ mix-blend-mode: soft-light;
+ opacity: 0.7;
+ pointer-events: none;
+ }
+
+ .card-shell {
+ position: relative;
+ z-index: 1;
+ padding: 24px;
+ border-radius: 32px;
+ background: linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.09),
+ rgba(255, 255, 255, 0.02)
+ );
+ border: 1px solid rgba(255, 255, 255, 0.35);
+ box-shadow: var(--shadow-soft);
+ backdrop-filter: blur(var(--blur-amount)) saturate(1.4);
+ -webkit-backdrop-filter: blur(var(--blur-amount)) saturate(1.4);
+ max-width: 390px;
+ width: 92vw;
+ }
+
+ .card {
+ position: relative;
+ border-radius: 26px;
+ overflow: hidden;
+ padding: 18px 20px 20px;
+ background: radial-gradient(
+ circle at 0% 0%,
+ rgba(255, 255, 255, 0.22),
+ transparent 55%
+ ),
+ radial-gradient(
+ circle at 100% 100%,
+ rgba(0, 0, 0, 0.4),
+ transparent 60%
+ ),
+ linear-gradient(
+ 145deg,
+ rgba(7, 15, 40, 0.96),
+ rgba(3, 7, 18, 0.98)
+ );
+ border: 1px solid rgba(255, 255, 255, 0.22);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
+ }
+
+ .card::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background-image: linear-gradient(
+ 120deg,
+ rgba(255, 255, 255, 0.22),
+ rgba(255, 255, 255, 0)
+ );
+ opacity: 0.32;
+ mix-blend-mode: soft-light;
+ pointer-events: none;
+ }
+
+ .card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ margin-bottom: 14px;
+ position: relative;
+ z-index: 1;
+ }
+
+ .city-block {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ }
+
+ .city-name {
+ font-weight: 600;
+ letter-spacing: 0.03em;
+ text-transform: uppercase;
+ font-size: 0.86rem;
+ opacity: 0.9;
+ }
+
+ .time {
+ font-size: 0.7rem;
+ opacity: 0.7;
+ text-transform: uppercase;
+ letter-spacing: 0.13em;
+ }
+
+ .badge {
+ padding: 4px 10px;
+ border-radius: 999px;
+ border: 1px solid rgba(255, 255, 255, 0.32);
+ background: radial-gradient(
+ circle at 0 0,
+ rgba(255, 255, 255, 0.34),
+ transparent 60%
+ ),
+ rgba(255, 255, 255, 0.08);
+ font-size: 0.68rem;
+ text-transform: uppercase;
+ letter-spacing: 0.16em;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ white-space: nowrap;
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.45);
+ }
+
+ .badge-dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #5df3a1;
+ box-shadow: 0 0 0 3px rgba(93, 243, 161, 0.25);
+ animation: badge-pulse 2.4s ease-out infinite;
+ }
+
+ @keyframes badge-pulse {
+ 0% {
+ transform: scale(1);
+ opacity: 1;
+ }
+ 80% {
+ transform: scale(1.7);
+ opacity: 0;
+ }
+ 100% {
+ transform: scale(1.7);
+ opacity: 0;
+ }
+ }
+
+ .card-main {
+ display: grid;
+ grid-template-columns: 1.1fr 1.2fr;
+ gap: 10px;
+ align-items: center;
+ position: relative;
+ z-index: 1;
+ }
+
+ .temp-block {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
+
+ .temp-row {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ }
+
+ .temp {
+ font-size: 3.1rem;
+ line-height: 1;
+ font-weight: 500;
+ letter-spacing: 0.06em;
+ text-shadow: 0 0 32px rgba(0, 0, 0, 0.7);
+ }
+
+ .temp .deg {
+ font-size: 1.4rem;
+ opacity: 0.9;
+ vertical-align: top;
+ margin-left: 1px;
+ }
+
+ .hi-lo {
+ display: flex;
+ flex-direction: column;
+ font-size: 0.7rem;
+ opacity: 0.76;
+ gap: 2px;
+ }
+
+ .hi-lo span {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ white-space: nowrap;
+ }
+
+ .hi-lo label {
+ opacity: 0.85;
+ font-size: 0.7rem;
+ }
+
+ .condition-text {
+ font-size: 0.86rem;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ opacity: 0.85;
+ }
+
+ .meta-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-top: 2px;
+ font-size: 0.7rem;
+ opacity: 0.78;
+ }
+
+ .meta-pill {
+ padding: 4px 8px;
+ border-radius: 999px;
+ border: 1px solid rgba(255, 255, 255, 0.22);
+ background: rgba(255, 255, 255, 0.04);
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .meta-dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #ffc76b;
+ }
+
+ .canvas-wrap {
+ position: relative;
+ height: 120px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ canvas {
+ width: 100%;
+ height: 100%;
+ border-radius: 24px;
+ background: radial-gradient(circle at 10% 0%, rgba(255, 255, 255, 0.35), transparent 60%),
+ radial-gradient(circle at 100% 100%, rgba(0, 0, 0, 0.7), transparent 60%);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
+ }
+
+ .canvas-glow {
+ position: absolute;
+ inset: -28%;
+ background: radial-gradient(circle at 50% 20%, rgba(255, 255, 255, 0.2), transparent 60%);
+ opacity: 0.55;
+ mix-blend-mode: soft-light;
+ pointer-events: none;
+ }
+
+ .card-footer {
+ margin-top: 14px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ position: relative;
+ z-index: 1;
+ font-size: 0.7rem;
+ opacity: 0.85;
+ }
+
+ .footer-left {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ }
+
+ .footer-left span {
+ white-space: nowrap;
+ }
+
+ .footer-left .hint {
+ opacity: 0.7;
+ font-size: 0.68rem;
+ }
+
+ button#cycle {
+ border: none;
+ outline: none;
+ cursor: pointer;
+ padding: 7px 12px;
+ border-radius: 999px;
+ background: linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.9),
+ rgba(255, 255, 255, 0.65)
+ );
+ color: #041322;
+ font-size: 0.72rem;
+ font-weight: 600;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ box-shadow: 0 10px 28px rgba(0, 0, 0, 0.6);
+ position: relative;
+ overflow: hidden;
+ transition: transform 0.12s ease-out, box-shadow 0.12s ease-out;
+ }
+
+ button#cycle span.icon {
+ font-size: 0.8rem;
+ display: inline-block;
+ transform-origin: center;
+ }
+
+ button#cycle::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ 120deg,
+ rgba(255, 255, 255, 0.7),
+ rgba(255, 255, 255, 0.15)
+ );
+ opacity: 0;
+ transform: translateX(-40%);
+ transition: opacity 0.35s ease-out, transform 0.35s ease-out;
+ pointer-events: none;
+ }
+
+ button#cycle:hover::before {
+ opacity: 1;
+ transform: translateX(0);
+ }
+
+ button#cycle:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 16px 30px rgba(0, 0, 0, 0.7);
+ }
+
+ button#cycle:active {
+ transform: translateY(1px) scale(0.98);
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.45);
+ }
+
+ .chip-row {
+ display: flex;
+ gap: 6px;
+ flex-wrap: wrap;
+ margin-top: 4px;
+ }
+
+ .chip {
+ font-size: 0.66rem;
+ padding: 3px 8px;
+ border-radius: 999px;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ background: rgba(0, 0, 0, 0.25);
+ opacity: 0.85;
+ }
+
+ @media (max-width: 400px) {
+ .card-shell {
+ padding: 18px;
+ border-radius: 26px;
+ }
+ .temp {
+ font-size: 2.7rem;
+ }
+ .card-main {
+ grid-template-columns: 1.15fr 1.1fr;
+ }
+ .canvas-wrap {
+ height: 110px;
+ }
+ }
+ </style>
+</head>
+<body>
+ <div class="bg-orbit"></div>
+ <div class="bg-noise"></div>
+
+ <div class="card-shell">
+ <div class="card">
+ <div class="card-header">
+ <div class="city-block">
+ <div class="city-name" id="cityName">Aurora City</div>
+ <div class="time" id="timeLabel">Now · Local Time</div>
+ </div>
+ <div class="badge">
+ <span class="badge-dot"></span>
+ <span id="badgeLabel">Live Weather</span>
+ </div>
+ </div>
+
+ <div class="card-main">
+ <div class="temp-block">
+ <div class="temp-row">
+ <div class="temp" id="temp">
+ 24<span class="deg">°C</span>
+ </div>
+ <div class="hi-lo">
+ <span><label>High</label> <span id="highTemp">26°</span></span>
+ <span><label>Low</label> <span id="lowTemp">18°</span></span>
+ </div>
+ </div>
+ <div class="condition-text" id="conditionText">Clear & bright</div>
+ <div class="meta-row">
+ <div class="meta-pill">
+ <span class="meta-dot"></span>
+ <span id="metaLabel">Feels like 25°</span>
+ </div>
+ <span id="extraLabel">Soft breeze · 7 km/h</span>
+ </div>
+ <div class="chip-row">
+ <div class="chip" id="chip1">Humidity 48%</div>
+ <div class="chip" id="chip2">Pressure 1014 hPa</div>
+ <div class="chip" id="chip3">UV Index 4</div>
+ </div>
+ </div>
+
+ <div class="canvas-wrap">
+ <canvas id="weatherCanvas"></canvas>
+ <div class="canvas-glow"></div>
+ </div>
+ </div>
+
+ <div class="card-footer">
+ <div class="footer-left">
+ <span id="footerLabel">Tap to cycle weather scenes</span>
+ <span class="hint" id="footerHint">Sunny · Cloudy · Rainy · Snowy</span>
+ </div>
+ <button id="cycle" type="button">
+ <span class="icon" id="cycleIcon">☀</span>
+ <span id="cycleLabel">Next</span>
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <script>
+ const canvas = document.getElementById("weatherCanvas");
+ const ctx = canvas.getContext("2d");
+
+ function resizeCanvas() {
+ const rect = canvas.getBoundingClientRect();
+ const ratio = window.devicePixelRatio || 1;
+ canvas.width = rect.width * ratio;
+ canvas.height = rect.height * ratio;
+ ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
+ }
+ resizeCanvas();
+ window.addEventListener("resize", resizeCanvas);
+
+ const states = [
+ {
+ key: "sunny",
+ city: "Aurora City",
+ temp: 24,
+ high: 26,
+ low: 18,
+ condition: "Clear & bright",
+ meta: "Feels like 25°",
+ extra: "Soft breeze · 7 km/h",
+ chips: ["Humidity 48%", "Pressure 1014 hPa", "UV Index 4"],
+ badge: "Live Weather",
+ icon: "☀",
+ },
+ {
+ key: "cloudy",
+ city: "Stratus Bay",
+ temp: 19,
+ high: 21,
+ low: 15,
+ condition: "Overcast clouds",
+ meta: "Feels like 18°",
+ extra: "Gentle wind · 10 km/h",
+ chips: ["Humidity 63%", "Pressure 1009 hPa", "Visibility 9 km"],
+ badge: "Layered Skies",
+ icon: "☁",
+ },
+ {
+ key: "rainy",
+ city: "Cascade Point",
+ temp: 16,
+ high: 18,
+ low: 13,
+ condition: "Light rain showers",
+ meta: "Feels like 14°",
+ extra: "Steady drizzle · 12 km/h",
+ chips: ["Humidity 92%", "Pressure 1002 hPa", "Rain 2.4 mm"],
+ badge: "Rain in Motion",
+ icon: "☂",
+ },
+ {
+ key: "snowy",
+ city: "Frosthaven",
+ temp: -2,
+ high: 0,
+ low: -6,
+ condition: "Soft falling snow",
+ meta: "Feels like -5°",
+ extra: "Calm air · 4 km/h",
+ chips: ["Humidity 78%", "Pressure 1018 hPa", "Snow 1.2 cm"],
+ badge: "Winter Glow",
+ icon: "❄",
+ },
+ ];
+
+ const cityEl = document.getElementById("cityName");
+ const tempEl = document.getElementById("temp");
+ const highEl = document.getElementById("highTemp");
+ const lowEl = document.getElementById("lowTemp");
+ const condEl = document.getElementById("conditionText");
+ const metaEl = document.getElementById("metaLabel");
+ const extraEl = document.getElementById("extraLabel");
+ const chip1El = document.getElementById("chip1");
+ const chip2El = document.getElementById("chip2");
+ const chip3El = document.getElementById("chip3");
+ const badgeEl = document.getElementById("badgeLabel");
+ const cycleBtn = document.getElementById("cycle");
+ const cycleIcon = document.getElementById("cycleIcon");
+ const footerLabel = document.getElementById("footerLabel");
+ const footerHint = document.getElementById("footerHint");
+ const timeLabel = document.getElementById("timeLabel");
+
+ let stateIndex = 0;
+ function applyState(index) {
+ const s = states[index];
+ cityEl.textContent = s.city;
+ tempEl.firstChild.nodeValue = s.temp.toString();
+ highEl.textContent = s.high + "°";
+ lowEl.textContent = s.low + "°";
+ condEl.textContent = s.condition;
+ metaEl.textContent = s.meta;
+ extraEl.textContent = s.extra;
+ chip1El.textContent = s.chips[0];
+ chip2El.textContent = s.chips[1];
+ chip3El.textContent = s.chips[2];
+ badgeEl.textContent = s.badge;
+ cycleIcon.textContent = s.icon;
+ footerLabel.textContent = "Scene: " + s.condition;
+ footerHint.textContent =
+ "Tap Next to switch to " + states[(index + 1) % states.length].condition;
+ }
+ applyState(stateIndex);
+
+ function updateTimeLabel() {
+ const now = new Date();
+ const h = now.getHours().toString().padStart(2, "0");
+ const m = now.getMinutes().toString().padStart(2, "0");
+ timeLabel.textContent = `Now · ${h}:${m}`;
+ }
+ updateTimeLabel();
+ setInterval(updateTimeLabel, 30000);
+
+ cycleBtn.addEventListener("click", () => {
+ stateIndex = (stateIndex + 1) % states.length;
+ applyState(stateIndex);
+ cycleIcon.style.transform = "rotate(20deg) scale(1.15)";
+ setTimeout(() => {
+ cycleIcon.style.transform = "rotate(0deg) scale(1)";
+ }, 200);
+ });
+
+ const sunRays = [];
+ const clouds = [];
+ const raindrops = [];
+ const snowflakes = [];
+
+ function createCloudLayer() {
+ clouds.length = 0;
+ const count = 7;
+ for (let i = 0; i < count; i++) {
+ clouds.push({
+ x: Math.random() * canvas.width,
+ y: 25 + Math.random() * 40,
+ w: 70 + Math.random() * 80,
+ h: 28 + Math.random() * 14,
+ speed: 0.4 + Math.random() * 0.4,
+ alpha: 0.23 + Math.random() * 0.18,
+ });
+ }
+ }
+
+ function createRain() {
+ raindrops.length = 0;
+ const count = 120;
+ for (let i = 0; i < count; i++) {
+ raindrops.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ len: 8 + Math.random() * 14,
+ speed: 2.2 + Math.random() * 2.2,
+ alpha: 0.4 + Math.random() * 0.3,
+ });
+ }
+ }
+
+ function createSnow() {
+ snowflakes.length = 0;
+ const count = 90;
+ for (let i = 0; i < count; i++) {
+ snowflakes.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ r: 1 + Math.random() * 2.2,
+ speed: 0.4 + Math.random() * 0.6,
+ drift: (Math.random() - 0.5) * 0.4,
+ phase: Math.random() * Math.PI * 2,
+ });
+ }
+ }
+
+ function createSunRays() {
+ sunRays.length = 0;
+ const count = 20;
+ for (let i = 0; i < count; i++) {
+ sunRays.push({
+ angle: (i / count) * Math.PI * 2,
+ length: 24 + Math.random() * 14,
+ width: 2 + Math.random() * 2,
+ });
+ }
+ }
+
+ createCloudLayer();
+ createRain();
+ createSnow();
+ createSunRays();
+
+ let lastTime = 0;
+ function animate(t) {
+ requestAnimationFrame(animate);
+ const dt = (t - lastTime) / 16.67 || 1;
+ lastTime = t;
+ const w = canvas.width;
+ const h = canvas.height;
+
+ ctx.clearRect(0, 0, w, h);
+
+ const stateKey = states[stateIndex].key;
+
+ // Shared sky gradient
+ let topColor, bottomColor;
+ if (stateKey === "sunny") {
+ topColor = "#5ad0ff";
+ bottomColor = "#0d1c3b";
+ } else if (stateKey === "cloudy") {
+ topColor = "#6f7b8f";
+ bottomColor = "#1a2435";
+ } else if (stateKey === "rainy") {
+ topColor = "#3f4f67";
+ bottomColor = "#111827";
+ } else {
+ topColor = "#415a7e";
+ bottomColor = "#0a1627";
+ }
+ const sky = ctx.createLinearGradient(0, 0, 0, h);
+ sky.addColorStop(0, topColor);
+ sky.addColorStop(1, bottomColor);
+ ctx.fillStyle = sky;
+ ctx.fillRect(0, 0, w, h);
+
+ // subtle horizon glow
+ const hg = ctx.createLinearGradient(0, h * 0.5, 0, h);
+ hg.addColorStop(0, "rgba(255,255,255,0.18)");
+ hg.addColorStop(1, "rgba(255,255,255,0)");
+ ctx.fillStyle = hg;
+ ctx.fillRect(0, h * 0.4, w, h * 0.6);
+
+ if (stateKey === "sunny") drawSunny(dt);
+ if (stateKey === "cloudy") drawCloudy(dt);
+ if (stateKey === "rainy") drawRainy(dt);
+ if (stateKey === "snowy") drawSnowy(dt);
+ }
+
+ function drawSunny(dt) {
+ const w = canvas.width;
+ const h = canvas.height;
+ const cx = w * 0.26;
+ const cy = h * 0.32;
+ const radius = 24;
+
+ ctx.save();
+ ctx.translate(cx, cy);
+
+ const pulse = 1 + Math.sin(performance.now() / 900) * 0.06;
+
+ // halo
+ const haloGrad = ctx.createRadialGradient(0, 0, 0, 0, 0, radius * 2.6);
+ haloGrad.addColorStop(0, "rgba(255,255,220,0.85)");
+ haloGrad.addColorStop(0.6, "rgba(255,220,120,0.22)");
+ haloGrad.addColorStop(1, "rgba(255,220,120,0)");
+ ctx.fillStyle = haloGrad;
+ ctx.beginPath();
+ ctx.arc(0, 0, radius * 2.6 * pulse, 0, Math.PI * 2);
+ ctx.fill();
+
+ // rays
+ const time = performance.now() / 2000;
+ for (let r of sunRays) {
+ const ang = r.angle + time * 0.6;
+ const inner = radius + 4;
+ const outer = radius + r.length;
+ ctx.strokeStyle = "rgba(255,240,210,0.7)";
+ ctx.lineWidth = r.width;
+ ctx.beginPath();
+ ctx.moveTo(Math.cos(ang) * inner, Math.sin(ang) * inner);
+ ctx.lineTo(Math.cos(ang) * outer, Math.sin(ang) * outer);
+ ctx.stroke();
+ }
+
+ // core
+ const coreGrad = ctx.createRadialGradient(0, 0, 0, 0, 0, radius);
+ coreGrad.addColorStop(0, "#fffbe8");
+ coreGrad.addColorStop(0.5, "#ffe47a");
+ coreGrad.addColorStop(1, "#ffc144");
+ ctx.fillStyle = coreGrad;
+ ctx.beginPath();
+ ctx.arc(0, 0, radius * pulse, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.restore();
+
+ // light decorative clouds
+ drawSoftClouds(dt, 0.25);
+ }
+
+ function drawSoftClouds(dt, intensity) {
+ const w = canvas.width;
+ const h = canvas.height;
+ for (let c of clouds) {
+ c.x += c.speed * dt * intensity;
+ if (c.x - c.w > w) c.x = -c.w;
+ ctx.save();
+ ctx.globalAlpha = c.alpha * 0.9;
+ ctx.fillStyle = "rgba(255,255,255,0.95)";
+ const x = c.x;
+ const y = c.y;
+ const rw = c.w;
+ const rh = c.h;
+ ctx.beginPath();
+ ctx.ellipse(x, y, rw * 0.42, rh * 0.55, 0, 0, Math.PI * 2);
+ ctx.ellipse(x - rw * 0.3, y + 4, rw * 0.3, rh * 0.5, 0, 0, Math.PI * 2);
+ ctx.ellipse(x + rw * 0.3, y + 3, rw * 0.36, rh * 0.52, 0, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+ }
+
+ function drawCloudy(dt) {
+ drawSoftClouds(dt, 1.1);
+ const w = canvas.width;
+ const h = canvas.height;
+ ctx.save();
+ ctx.globalAlpha = 0.55;
+ const g = ctx.createLinearGradient(0, h * 0.45, 0, h);
+ g.addColorStop(0, "rgba(0,0,0,0.15)");
+ g.addColorStop(1, "rgba(0,0,0,0.5)");
+ ctx.fillStyle = g;
+ ctx.beginPath();
+ ctx.roundRect(0, h * 0.62, w, h * 0.5, 40);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ function drawRainy(dt) {
+ const w = canvas.width;
+ const h = canvas.height;
+
+ // heavy cloud layer
+ for (let c of clouds) {
+ c.x += c.speed * dt * 0.8;
+ if (c.x - c.w > w) c.x = -c.w;
+ ctx.save();
+ ctx.globalAlpha = 0.65;
+ ctx.fillStyle = "#d5d9e2";
+ const x = c.x;
+ const y = c.y + 4;
+ const rw = c.w * 1.15;
+ const rh = c.h * 1.05;
+ ctx.beginPath();
+ ctx.ellipse(x, y, rw * 0.45, rh * 0.6, 0, 0, Math.PI * 2);
+ ctx.ellipse(x - rw * 0.3, y + 6, rw * 0.32, rh * 0.54, 0, 0, Math.PI * 2);
+ ctx.ellipse(x + rw * 0.35, y + 4, rw * 0.38, rh * 0.6, 0, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ ctx.save();
+ ctx.lineCap = "round";
+ for (let r of raindrops) {
+ r.y += r.speed * dt * 1.4;
+ r.x += 0.5 * dt;
+ if (r.y > h) {
+ r.y = -20;
+ r.x = Math.random() * w;
+ }
+ if (r.x > w) r.x = 0;
+ ctx.strokeStyle = "rgba(201,221,255," + r.alpha + ")";
+ ctx.lineWidth = 1.2;
+ ctx.beginPath();
+ ctx.moveTo(r.x, r.y);
+ ctx.lineTo(r.x - 1, r.y + r.len);
+ ctx.stroke();
+ }
+ ctx.restore();
+
+ ctx.save();
+ ctx.globalAlpha = 0.3;
+ const g = ctx.createLinearGradient(0, h * 0.4, 0, h);
+ g.addColorStop(0, "rgba(0,0,0,0.1)");
+ g.addColorStop(1, "rgba(0,0,0,0.55)");
+ ctx.fillStyle = g;
+ ctx.beginPath();
+ ctx.roundRect(0, h * 0.64, w, h * 0.6, 32);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ function drawSnowy(dt) {
+ const w = canvas.width;
+ const h = canvas.height;
+
+ // cool cloud cap
+ for (let c of clouds) {
+ c.x += c.speed * dt * 0.5;
+ if (c.x - c.w > w) c.x = -c.w;
+ ctx.save();
+ ctx.globalAlpha = 0.8;
+ ctx.fillStyle = "#eff3ff";
+ const x = c.x;
+ const y = c.y + 2;
+ const rw = c.w * 1.05;
+ const rh = c.h;
+ ctx.beginPath();
+ ctx.ellipse(x, y, rw * 0.45, rh * 0.62, 0, 0, Math.PI * 2);
+ ctx.ellipse(x - rw * 0.35, y + 5, rw * 0.34, rh * 0.52, 0, 0, Math.PI * 2);
+ ctx.ellipse(x + rw * 0.36, y + 4, rw * 0.35, rh * 0.6, 0, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ for (let s of snowflakes) {
+ s.y += s.speed * dt * 0.85;
+ s.phase += 0.02 * dt;
+ s.x += Math.sin(s.phase) * 0.4 * dt + s.drift * dt;
+ if (s.y > h) {
+ s.y = -10;
+ s.x = Math.random() * w;
+ }
+ if (s.x < -10) s.x = w + 5;
+ if (s.x > w + 10) s.x = -5;
+ ctx.save();
+ ctx.fillStyle = "rgba(255,255,255,0.95)";
+ ctx.beginPath();
+ ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ ctx.save();
+ ctx.globalAlpha = 0.9;
+ const g = ctx.createLinearGradient(0, h * 0.6, 0, h);
+ g.addColorStop(0, "rgba(255,255,255,0.4)");
+ g.addColorStop(1, "rgba(255,255,255,0.9)");
+ ctx.fillStyle = g;
+ ctx.beginPath();
+ ctx.roundRect(-10, h * 0.74, w + 20, h * 0.5, 36);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ requestAnimationFrame(animate);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/b14c4e0ccc4e/gpt.png b/data/artifacts/b14c4e0ccc4e/gpt.png
new file mode 100644
index 0000000..0261327
Binary files /dev/null and b/data/artifacts/b14c4e0ccc4e/gpt.png differ
diff --git a/data/artifacts/b14c4e0ccc4e/grok.html b/data/artifacts/b14c4e0ccc4e/grok.html
new file mode 100644
index 0000000..2ff0ba2
--- /dev/null
+++ b/data/artifacts/b14c4e0ccc4e/grok.html
@@ -0,0 +1,492 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Weather Card</title>
+<style>
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+ body {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
+ background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
+ overflow: hidden;
+ }
+ .bg-orb {
+ position: fixed;
+ border-radius: 50%;
+ filter: blur(80px);
+ opacity: 0.45;
+ pointer-events: none;
+ transition: background 1.2s ease;
+ }
+ .bg-orb.a { width: 420px; height: 420px; top: -80px; left: -60px; }
+ .bg-orb.b { width: 360px; height: 360px; bottom: -100px; right: -40px; }
+ .card {
+ position: relative;
+ width: 340px;
+ padding: 36px 32px 28px;
+ border-radius: 28px;
+ background: rgba(255, 255, 255, 0.12);
+ backdrop-filter: blur(24px) saturate(160%);
+ -webkit-backdrop-filter: blur(24px) saturate(160%);
+ border: 1px solid rgba(255, 255, 255, 0.28);
+ box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.35),
+ inset 0 1px 0 rgba(255, 255, 255, 0.25);
+ color: #fff;
+ text-align: center;
+ overflow: hidden;
+ z-index: 1;
+ }
+ .card::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ border-radius: 28px;
+ background: linear-gradient(145deg, rgba(255,255,255,0.18), transparent 50%, rgba(255,255,255,0.05));
+ pointer-events: none;
+ }
+ canvas {
+ display: block;
+ width: 160px;
+ height: 160px;
+ margin: 0 auto 8px;
+ }
+ .city {
+ font-size: 1.35rem;
+ font-weight: 600;
+ letter-spacing: 0.04em;
+ opacity: 0.95;
+ margin-bottom: 4px;
+ }
+ .temp {
+ font-size: 3.6rem;
+ font-weight: 200;
+ line-height: 1.1;
+ letter-spacing: -0.02em;
+ }
+ .temp span {
+ font-size: 1.6rem;
+ vertical-align: super;
+ opacity: 0.8;
+ }
+ .condition {
+ font-size: 1.05rem;
+ font-weight: 400;
+ opacity: 0.85;
+ margin: 6px 0 22px;
+ letter-spacing: 0.06em;
+ text-transform: capitalize;
+ min-height: 1.4em;
+ transition: opacity 0.3s;
+ }
+ .btn {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 12px 28px;
+ border: 1px solid rgba(255, 255, 255, 0.35);
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.15);
+ color: #fff;
+ font-size: 0.95rem;
+ font-weight: 500;
+ letter-spacing: 0.03em;
+ cursor: pointer;
+ backdrop-filter: blur(8px);
+ transition: background 0.25s, transform 0.2s, box-shadow 0.25s;
+ outline: none;
+ }
+ .btn:hover {
+ background: rgba(255, 255, 255, 0.28);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
+ transform: translateY(-1px);
+ }
+ .btn:active { transform: translateY(0) scale(0.98); }
+ .dots {
+ display: flex;
+ justify-content: center;
+ gap: 8px;
+ margin-top: 18px;
+ }
+ .dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.3);
+ transition: background 0.3s, transform 0.3s;
+ }
+ .dot.active {
+ background: #fff;
+ transform: scale(1.25);
+ }
+ .meta {
+ display: flex;
+ justify-content: center;
+ gap: 24px;
+ margin-top: 18px;
+ font-size: 0.8rem;
+ opacity: 0.7;
+ }
+ .meta div { display: flex; flex-direction: column; gap: 2px; }
+ .meta strong { font-weight: 600; opacity: 1; font-size: 0.95rem; color: #fff; }
+</style>
+</head>
+<body>
+ <div class="bg-orb a" id="orbA"></div>
+ <div class="bg-orb b" id="orbB"></div>
+ <div class="card">
+ <canvas id="wx" width="320" height="320"></canvas>
+ <div class="city" id="city">San Francisco</div>
+ <div class="temp" id="temp">72<span>°</span></div>
+ <div class="condition" id="condition">Sunny</div>
+ <button class="btn" id="cycle" type="button">Next Weather</button>
+ <div class="dots" id="dots"></div>
+ <div class="meta">
+ <div>Humidity<strong id="hum">42%</strong></div>
+ <div>Wind<strong id="wind">8 mph</strong></div>
+ <div>Feels<strong id="feels">74°</strong></div>
+ </div>
+ </div>
+<script>
+(function () {
+ const states = [
+ {
+ name: 'Sunny',
+ city: 'Los Angeles',
+ temp: 84,
+ feels: 86,
+ hum: '28%',
+ wind: '6 mph',
+ orbA: '#f6d365',
+ orbB: '#fda085'
+ },
+ {
+ name: 'Cloudy',
+ city: 'Seattle',
+ temp: 58,
+ feels: 55,
+ hum: '72%',
+ wind: '12 mph',
+ orbA: '#89f7fe',
+ orbB: '#66a6ff'
+ },
+ {
+ name: 'Rainy',
+ city: 'London',
+ temp: 52,
+ feels: 48,
+ hum: '88%',
+ wind: '15 mph',
+ orbA: '#4b6cb7',
+ orbB: '#182848'
+ },
+ {
+ name: 'Snowy',
+ city: 'Reykjavik',
+ temp: 28,
+ feels: 22,
+ hum: '78%',
+ wind: '10 mph',
+ orbA: '#e0c3fc',
+ orbB: '#8ec5fc'
+ }
+ ];
+
+ let idx = 0;
+ const canvas = document.getElementById('wx');
+ const ctx = canvas.getContext('2d');
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
+ canvas.width = 320 * dpr;
+ canvas.height = 320 * dpr;
+ ctx.scale(dpr, dpr);
+ const W = 320, H = 320, CX = 160, CY = 150;
+
+ const cityEl = document.getElementById('city');
+ const tempEl = document.getElementById('temp');
+ const condEl = document.getElementById('condition');
+ const humEl = document.getElementById('hum');
+ const windEl = document.getElementById('wind');
+ const feelsEl = document.getElementById('feels');
+ const orbA = document.getElementById('orbA');
+ const orbB = document.getElementById('orbB');
+ const dotsEl = document.getElementById('dots');
+
+ states.forEach((_, i) => {
+ const d = document.createElement('div');
+ d.className = 'dot' + (i === 0 ? ' active' : '');
+ dotsEl.appendChild(d);
+ });
+
+ // Particles
+ const drops = Array.from({ length: 50 }, () => ({
+ x: Math.random() * W,
+ y: Math.random() * H,
+ len: 12 + Math.random() * 16,
+ spd: 4 + Math.random() * 5,
+ op: 0.3 + Math.random() * 0.5
+ }));
+ const flakes = Array.from({ length: 45 }, () => ({
+ x: Math.random() * W,
+ y: Math.random() * H,
+ r: 1.5 + Math.random() * 3.5,
+ spd: 0.6 + Math.random() * 1.4,
+ drift: Math.random() * Math.PI * 2,
+ dspd: 0.01 + Math.random() * 0.02,
+ op: 0.5 + Math.random() * 0.5
+ }));
+ const clouds = [
+ { x: 40, y: 100, s: 1.1, spd: 0.25 },
+ { x: 180, y: 130, s: 0.85, spd: 0.18 },
+ { x: 100, y: 160, s: 0.7, spd: 0.22 },
+ { x: 240, y: 90, s: 0.95, spd: 0.15 }
+ ];
+
+ let sunAngle = 0;
+ let t = 0;
+
+ function drawSun(alpha) {
+ ctx.save();
+ ctx.globalAlpha = alpha;
+ // glow
+ const g = ctx.createRadialGradient(CX, CY, 10, CX, CY, 90);
+ g.addColorStop(0, 'rgba(255,220,100,0.9)');
+ g.addColorStop(0.4, 'rgba(255,180,50,0.35)');
+ g.addColorStop(1, 'rgba(255,160,40,0)');
+ ctx.fillStyle = g;
+ ctx.beginPath();
+ ctx.arc(CX, CY, 90, 0, Math.PI * 2);
+ ctx.fill();
+ // rays
+ ctx.translate(CX, CY);
+ ctx.rotate(sunAngle);
+ for (let i = 0; i < 12; i++) {
+ ctx.rotate(Math.PI / 6);
+ const pulse = 1 + Math.sin(t * 2 + i) * 0.08;
+ ctx.beginPath();
+ ctx.moveTo(42, -4);
+ ctx.lineTo(68 * pulse, 0);
+ ctx.lineTo(42, 4);
+ ctx.fillStyle = 'rgba(255,210,80,0.75)';
+ ctx.fill();
+ }
+ ctx.rotate(-sunAngle);
+ // core
+ const core = ctx.createRadialGradient(0, 0, 0, 0, 0, 36);
+ core.addColorStop(0, '#fff6c8');
+ core.addColorStop(0.6, '#ffd060');
+ core.addColorStop(1, '#ffb030');
+ ctx.fillStyle = core;
+ ctx.beginPath();
+ ctx.arc(0, 0, 34, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ function drawCloud(c, alpha, gray) {
+ ctx.save();
+ ctx.globalAlpha = alpha * 0.9;
+ const x = c.x, y = c.y, s = c.s;
+ ctx.translate(x, y);
+ ctx.scale(s, s);
+ const col = gray
+ ? 'rgba(180,190,210,0.85)'
+ : 'rgba(255,255,255,0.88)';
+ ctx.fillStyle = col;
+ ctx.beginPath();
+ ctx.arc(0, 0, 28, 0, Math.PI * 2);
+ ctx.arc(32, -8, 36, 0, Math.PI * 2);
+ ctx.arc(68, 2, 26, 0, Math.PI * 2);
+ ctx.arc(38, 16, 24, 0, Math.PI * 2);
+ ctx.arc(10, 14, 20, 0, Math.PI * 2);
+ ctx.fill();
+ // soft highlight
+ ctx.fillStyle = 'rgba(255,255,255,0.25)';
+ ctx.beginPath();
+ ctx.ellipse(28, -14, 22, 12, -0.3, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+
+ function drawRain(alpha) {
+ ctx.save();
+ ctx.globalAlpha = alpha;
+ ctx.strokeStyle = 'rgba(180,210,255,0.7)';
+ ctx.lineWidth = 1.5;
+ ctx.lineCap = 'round';
+ drops.forEach(d => {
+ ctx.globalAlpha = alpha * d.op;
+ ctx.beginPath();
+ ctx.moveTo(d.x, d.y);
+ ctx.lineTo(d.x - 2, d.y + d.len);
+ ctx.stroke();
+ d.y += d.spd;
+ d.x -= 0.4;
+ if (d.y > H + 20) {
+ d.y = -20;
+ d.x = Math.random() * W;
+ }
+ });
+ ctx.restore();
+ }
+
+ function drawSnow(alpha) {
+ ctx.save();
+ flakes.forEach(f => {
+ ctx.globalAlpha = alpha * f.op;
+ f.drift += f.dspd;
+ f.x += Math.sin(f.drift) * 0.6;
+ f.y += f.spd;
+ if (f.y > H + 10) {
+ f.y = -10;
+ f.x = Math.random() * W;
+ }
+ if (f.x < -10) f.x = W + 10;
+ if (f.x > W + 10) f.x = -10;
+ ctx.fillStyle = '#fff';
+ ctx.beginPath();
+ ctx.arc(f.x, f.y, f.r, 0, Math.PI * 2);
+ ctx.fill();
+ // tiny cross sparkle
+ if (f.r > 3) {
+ ctx.strokeStyle = 'rgba(255,255,255,0.6)';
+ ctx.lineWidth = 0.8;
+ ctx.beginPath();
+ ctx.moveTo(f.x - f.r, f.y);
+ ctx.lineTo(f.x + f.r, f.y);
+ ctx.moveTo(f.x, f.y - f.r);
+ ctx.lineTo(f.x, f.y + f.r);
+ ctx.stroke();
+ }
+ });
+ ctx.restore();
+ }
+
+ function lerp(a, b, p) { return a + (b - a) * p; }
+
+ // Crossfade alphas
+ let alphas = [1, 0, 0, 0];
+ let target = [1, 0, 0, 0];
+
+ function applyState(i, animate) {
+ const s = states[i];
+ target = [0, 0, 0, 0];
+ target[i] = 1;
+ cityEl.textContent = s.city;
+ tempEl.innerHTML = s.temp + '<span>°</span>';
+ condEl.style.opacity = '0';
+ setTimeout(() => {
+ condEl.textContent = s.name;
+ condEl.style.opacity = '1';
+ }, animate ? 150 : 0);
+ humEl.textContent = s.hum;
+ windEl.textContent = s.wind;
+ feelsEl.textContent = s.feels + '°';
+ orbA.style.background = s.orbA;
+ orbB.style.background = s.orbB;
+ dotsEl.querySelectorAll('.dot').forEach((d, k) => {
+ d.classList.toggle('active', k === i);
+ });
+ }
+
+ applyState(0, false);
+
+ document.getElementById('cycle').addEventListener('click', () => {
+ idx = (idx + 1) % states.length;
+ applyState(idx, true);
+ });
+
+ function frame() {
+ t += 0.016;
+ sunAngle += 0.008;
+ ctx.clearRect(0, 0, W, H);
+
+ for (let i = 0; i < 4; i++) {
+ alphas[i] = lerp(alphas[i], target[i], 0.06);
+ }
+
+ // Sunny
+ if (alphas[0] > 0.01) drawSun(alphas[0]);
+
+ // Cloudy (+ light sun behind if transitioning)
+ if (alphas[1] > 0.01) {
+ if (alphas[1] > 0.3) {
+ ctx.save();
+ ctx.globalAlpha = alphas[1] * 0.35;
+ const g = ctx.createRadialGradient(CX + 40, CY - 30, 5, CX + 40, CY - 30, 50);
+ g.addColorStop(0, 'rgba(255,230,150,0.8)');
+ g.addColorStop(1, 'rgba(255,200,100,0)');
+ ctx.fillStyle = g;
+ ctx.beginPath();
+ ctx.arc(CX + 40, CY - 30, 50, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+ clouds.forEach(c => {
+ c.x += c.spd;
+ if (c.x > W + 80) c.x = -100;
+ drawCloud(c, alphas[1], false);
+ });
+ }
+
+ // Rainy
+ if (alphas[2] > 0.01) {
+ clouds.forEach((c, i) => {
+ const cc = { x: c.x * 0.9 + i * 10, y: c.y - 20, s: c.s * 1.05, spd: c.spd };
+ if (i === 0) {
+ // move shared clouds only once for rain look when not cloudy
+ }
+ drawCloud({
+ x: ((c.x + i * 30) % (W + 120)) - 40,
+ y: 70 + i * 22,
+ s: 0.9 + i * 0.08,
+ spd: 0
+ }, alphas[2], true);
+ });
+ // drift rain clouds slowly
+ clouds.forEach(c => {
+ if (alphas[1] < 0.5) {
+ c.x += c.spd * 0.5;
+ if (c.x > W + 80) c.x = -100;
+ }
+ });
+ drawRain(alphas[2]);
+ }
+
+ // Snowy
+ if (alphas[3] > 0.01) {
+ clouds.forEach((c, i) => {
+ drawCloud({
+ x: ((c.x * 0.7 + i * 50) % (W + 100)) - 30,
+ y: 60 + (i % 3) * 28,
+ s: 0.75 + (i % 3) * 0.12,
+ spd: 0
+ }, alphas[3] * 0.7, false);
+ });
+ drawSnow(alphas[3]);
+ }
+
+ // Keep clouds drifting on sunny lightly
+ if (alphas[0] > 0.5) {
+ clouds.forEach(c => {
+ c.x += c.spd * 0.3;
+ if (c.x > W + 80) c.x = -100;
+ });
+ // faint distant clouds
+ ctx.save();
+ drawCloud({ x: clouds[1].x, y: 200, s: 0.5, spd: 0 }, alphas[0] * 0.35, false);
+ ctx.restore();
+ }
+
+ requestAnimationFrame(frame);
+ }
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/b14c4e0ccc4e/grok.png b/data/artifacts/b14c4e0ccc4e/grok.png
new file mode 100644
index 0000000..d16e3c3
Binary files /dev/null and b/data/artifacts/b14c4e0ccc4e/grok.png differ
diff --git a/data/artifacts/b14c4e0ccc4e/hermes3-8b.html b/data/artifacts/b14c4e0ccc4e/hermes3-8b.html
new file mode 100644
index 0000000..8b3e9da
--- /dev/null
+++ b/data/artifacts/b14c4e0ccc4e/hermes3-8b.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Weather Animation</title>
+ <style>
+ body {
+ background-color: #f0f5ed;
+ font-family: Arial, sans-serif;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ }
+
+ .card {
+ background: rgba(255, 255, 255, 0.3);
+ border-radius: 20px;
+ box-shadow: inset 3px 3px 15px #a8a8a8, 5px 5px 40px #f0f5ed;
+ padding: 30px;
+ text-align: center;
+ }
+
+ .city {
+ font-size: 24px;
+ margin-bottom: 20px;
+ }
+
+ .temperature {
+ font-size: 48px;
+ margin-bottom: 15px;
+ }
+
+ .condition {
+ font-size: 18px;
+ opacity: 0;
+ animation: fadeIn 1s linear 0.5s forwards;
+ }
+
+ @keyframes fadeIn {
+ from {opacity: 0;}
+ to {opacity: 1;}
+ }
+
+ canvas {
+ position: absolute;
+ top: -50px;
+ left: 50%;
+ transform: translateX(-50%);
+ z-index: -1;
+ }
+
+ button {
+ background-color: #f5a623;
+ border: none;
+ border-radius: 15px;
+ color: white;
+ cursor: pointer;
+ font-size: 18px;
+ margin-top: 30px;
+ padding: 10px 25px;
+ transition: background-color 0.3s ease-in-out;
+ }
+
+ button:hover {
+ background-color: #f48b23;
+ }
+ </style>
+</head>
+<body>
+ <div class="card">
+ <span class="city">New York</span>
+ <canvas id="weatherCanvas" width="300" height="200"></canvas>
+ <span class="temperature">25°C</span>
+ <span class="condition">Sunny</span>
+ <button onclick="changeWeather()">Change Weather</button>
+ </div>
+
+ <script>
+ const weatherStates = [
+ { temperature: 25, condition: 'Sunny', animation: sunny },
+ { temperature: 18, condition: 'Cloudy', animation: cloudy },
+ { temperature: 10, condition: 'Rainy', animation: rainy },
+ { temperature: 5, condition: 'Snowy', animation: snowy }
+ ];
+
+ let currentWeatherIndex = 0;
+
+ function changeWeather() {
+ currentWeatherIndex = (currentWeatherIndex + 1) % weatherStates.length;
+ updateWeather();
+ }
+
+ function updateWeather() {
+ const { temperature, condition, animation } = weatherStates[currentWeatherIndex];
+ document.querySelector('.temperature').textContent = `${temperature}°C`;
+ document.querySelector('.condition').textContent = condition;
+
+ animation(canvas);
+ }
+
+ function sunny(canvas) {
+ // Animation for sunny weather
+ // ...
+ }
+
+ function cloudy(canvas) {
+ // Animation for cloudy weather
+ // ...
+ }
+
+ function rainy(canvas) {
+ // Animation for rainy weather
+ // ...
+ }
+
+ function snowy(canvas) {
+ // Animation for snowy weather
+ // ...
+ }
+
+ updateWeather();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/b14c4e0ccc4e/hermes3-8b.png b/data/artifacts/b14c4e0ccc4e/hermes3-8b.png
new file mode 100644
index 0000000..822b776
Binary files /dev/null and b/data/artifacts/b14c4e0ccc4e/hermes3-8b.png differ
diff --git a/data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.html b/data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..00f6852
--- /dev/null
+++ b/data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Weather Card</title>
+<style>
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ background: #e9ecef;
+ margin: 0;
+ font-family: Arial, sans-serif;
+ }
+ .card {
+ width: 300px;
+ padding: 20px;
+ border-radius: 15px;
+ background: rgba(255, 255, 255, 0.2);
+ backdrop-filter: blur(10px);
+ box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
+ text-align: center;
+ position: relative;
+ overflow: hidden;
+ }
+ .city {
+ font-size: 2em;
+ margin-bottom: 10px;
+ }
+ .temperature {
+ font-size: 3em;
+ margin-bottom: 20px;
+ font-weight: bold;
+ }
+ .condition {
+ font-size: 1.5em;
+ color: #6c757d;
+ }
+ canvas {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: -1;
+ }
+ button {
+ padding: 10px 20px;
+ border: none;
+ border-radius: 5px;
+ background: #4CAF50;
+ color: white;
+ cursor: pointer;
+ font-size: 1em;
+ transition: background 0.3s;
+ }
+ button:hover {
+ background: #45a049;
+ }
+</style>
+</head>
+<body>
+<div class="card">
+ <div class="city" id="city">City</div>
+ <div class="temperature" id="temperature">22°C</div>
+ <div class="condition" id="condition">Sunny</div>
+ <button onclick="changeWeather()">Change Weather</button>
+ <canvas id="weatherCanvas"></canvas>
+</div>
+
+<script>
+const weatherStates = [
+ { city: "City", temp: "22°C", condition: "Sunny", draw: drawSun },
+ { city: "Beach", temp: "30°C", condition: "Cloudy", draw: drawClouds },
+ { city: "Rainbow", temp: "15°C", condition: "Raining", draw: drawRain },
+ { city: "Snowy Mountain", temp: "-5°C", condition: "Snowing", draw: drawSnow }
+];
+
+let currentState = 0;
+
+function changeWeather() {
+ currentState = (currentState + 1) % weatherStates.length;
+ updateWeather();
+}
+
+function updateWeather() {
+ const state = weatherStates[currentState];
+ document.getElementById('city').textContent = state.city;
+ document.getElementById('temperature').textContent = state.temp;
+ document.getElementById('condition').textContent = state.condition;
+ state.draw();
+}
+
+function drawSun() {
+ const canvas = document.getElementById('weatherCanvas');
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.beginPath();
+ ctx.arc(canvas.width / 2, canvas.height / 3, 50, 0, Math.PI * 2);
+ ctx.fillStyle = 'yellow';
+ ctx.fill();
+
+ for (let i = 0; i < 12; i++) {
+ ctx.beginPath();
+ ctx.moveTo(canvas.width / 2 + 60 * Math.cos(i * Math.PI / 6), canvas.height / 3 + 60 * Math.sin(i * Math.PI / 6));
+ ctx.lineTo(canvas.width / 2 + 85 * Math.cos(i * Math.PI / 6), canvas.height / 3 + 85 * Math.sin(i * Math.PI / 6));
+ ctx.strokeStyle = 'yellow';
+ ctx.lineWidth = 4;
+ ctx.stroke();
+ }
+}
+
+function drawClouds() {
+ const canvas = document.getElementById('weatherCanvas');
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ function drawCloud(x, y) {
+ ctx.beginPath();
+ ctx.arc(x, y, 30, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(x + 40, y - 15, 30, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(x + 80, y, 30, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ ctx.fillStyle = 'gray';
+ drawCloud(50, canvas.height / 2.5);
+ drawCloud(canvas.width - 130, canvas.height / 2.5);
+}
+
+function drawRain() {
+ const canvas = document.getElementById('weatherCanvas');
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ function drawDrop(x, y) {
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + 1, y + 8);
+ ctx.lineTo(x - 1, y + 8);
+ ctx.fill();
+ }
+
+ ctx.fillStyle = 'blue';
+ for (let i = 0; i < 50; i++) {
+ drawDrop(Math.random() * canvas.width, Math.random() * canvas.height / 2);
+ }
+}
+
+function drawSnow() {
+ const canvas = document.getElementById('weatherCanvas');
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ function drawFlake(x, y) {
+ ctx.beginPath();
+ ctx.arc(x, y, 1.5, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ ctx.fillStyle = 'white';
+ for (let i = 0; i < 100; i++) {
+ drawFlake(Math.random() * canvas.width, Math.random() * canvas.height / 2);
+ }
+}
+
+updateWeather();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.png b/data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..3fb407a
Binary files /dev/null and b/data/artifacts/b14c4e0ccc4e/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/b14c4e0ccc4e/kimi.html b/data/artifacts/b14c4e0ccc4e/kimi.html
new file mode 100644
index 0000000..a2f32bd
--- /dev/null
+++ b/data/artifacts/b14c4e0ccc4e/kimi.html
@@ -0,0 +1,397 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Glassmorphism Weather Card</title>
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ transition: background 0.8s ease;
+ }
+
+ body.sunny-bg { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
+ body.cloudy-bg { background: linear-gradient(135deg, #bdc3c7 0%, #2c3e50 100%); }
+ body.rainy-bg { background: linear-gradient(135deg, #373b44 0%, #4286f4 100%); }
+ body.snowy-bg { background: linear-gradient(135deg, #e6dada 0%, #274046 100%); }
+
+ .weather-card {
+ position: relative;
+ width: 350px;
+ height: 500px;
+ background: rgba(255, 255, 255, 0.15);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ border-radius: 30px;
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.2);
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 40px 20px;
+ z-index: 10;
+ }
+
+ #canvas {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 1;
+ }
+
+ .weather-icon-container {
+ position: relative;
+ width: 150px;
+ height: 150px;
+ margin-bottom: 30px;
+ z-index: 2;
+ }
+
+ .sun {
+ position: absolute;
+ width: 80px;
+ height: 80px;
+ background: radial-gradient(circle, #ffd700 0%, #ff8c00 100%);
+ border-radius: 50%;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ box-shadow: 0 0 40px #ffd700, 0 0 80px rgba(255, 140, 0, 0.5);
+ opacity: 0;
+ transition: opacity 0.5s;
+ }
+
+ .sun.active { opacity: 1; }
+
+ .sun-rays {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ animation: rotate 12s linear infinite;
+ }
+
+ .ray {
+ position: absolute;
+ width: 4px;
+ height: 25px;
+ background: linear-gradient(to bottom, #ffd700, transparent);
+ left: 50%;
+ top: -20px;
+ transform-origin: 50% 95px;
+ border-radius: 2px;
+ }
+
+ .cloud {
+ position: absolute;
+ background: rgba(255, 255, 255, 0.9);
+ border-radius: 100px;
+ opacity: 0;
+ transition: opacity 0.5s;
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
+ }
+
+ .cloud.active { opacity: 1; }
+
+ .cloud::before {
+ content: '';
+ position: absolute;
+ background: rgba(255, 255, 255, 0.9);
+ border-radius: 100px;
+ }
+
+ .cloud1 {
+ width: 100px;
+ height: 40px;
+ top: 40%;
+ left: 10%;
+ animation: float 6s ease-in-out infinite;
+ }
+
+ .cloud1::before {
+ width: 50px;
+ height: 50px;
+ top: -25px;
+ left: 10px;
+ }
+
+ .cloud2 {
+ width: 80px;
+ height: 35px;
+ top: 60%;
+ right: 10%;
+ animation: float 8s ease-in-out infinite reverse;
+ }
+
+ .cloud2::before {
+ width: 60px;
+ height: 40px;
+ top: -20px;
+ right: 15px;
+ }
+
+ .info {
+ text-align: center;
+ color: white;
+ z-index: 2;
+ margin-top: 20px;
+ }
+
+ .city {
+ font-size: 32px;
+ font-weight: 300;
+ letter-spacing: 2px;
+ margin-bottom: 10px;
+ text-shadow: 0 2px 10px rgba(0,0,0,0.2);
+ }
+
+ .temp {
+ font-size: 72px;
+ font-weight: 200;
+ margin-bottom: 10px;
+ text-shadow: 0 2px 10px rgba(0,0,0,0.2);
+ position: relative;
+ }
+
+ .temp::after {
+ content: '°';
+ position: absolute;
+ top: 10px;
+ right: -25px;
+ font-size: 40px;
+ font-weight: 300;
+ }
+
+ .condition {
+ font-size: 20px;
+ text-transform: uppercase;
+ letter-spacing: 3px;
+ opacity: 0.9;
+ margin-bottom: 40px;
+ font-weight: 500;
+ }
+
+ .cycle-btn {
+ padding: 15px 40px;
+ background: rgba(255, 255, 255, 0.2);
+ border: 1px solid rgba(255, 255, 255, 0.4);
+ border-radius: 30px;
+ color: white;
+ font-size: 16px;
+ cursor: pointer;
+ transition: all 0.3s;
+ backdrop-filter: blur(5px);
+ z-index: 2;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ font-weight: 600;
+ }
+
+ .cycle-btn:hover {
+ background: rgba(255, 255, 255, 0.3);
+ transform: translateY(-2px);
+ box-shadow: 0 10px 20px rgba(0,0,0,0.2);
+ }
+
+ .cycle-btn:active {
+ transform: translateY(0);
+ }
+
+ @keyframes rotate {
+ from { transform: rotate(0deg); }
+ to { transform: rotate(360deg); }
+ }
+
+ @keyframes float {
+ 0%, 100% { transform: translateY(0px) translateX(0px); }
+ 33% { transform: translateY(-10px) translateX(5px); }
+ 66% { transform: translateY(5px) translateX(-5px); }
+ }
+ </style>
+</head>
+<body class="sunny-bg">
+ <div class="weather-card">
+ <canvas id="canvas"></canvas>
+
+ <div class="weather-icon-container">
+ <div class="sun" id="sun">
+ <div class="sun-rays">
+ <div class="ray" style="transform: rotate(0deg)"></div>
+ <div class="ray" style="transform: rotate(45deg)"></div>
+ <div class="ray" style="transform: rotate(90deg)"></div>
+ <div class="ray" style="transform: rotate(135deg)"></div>
+ <div class="ray" style="transform: rotate(180deg)"></div>
+ <div class="ray" style="transform: rotate(225deg)"></div>
+ <div class="ray" style="transform: rotate(270deg)"></div>
+ <div class="ray" style="transform: rotate(315deg)"></div>
+ </div>
+ </div>
+ <div class="cloud cloud1" id="cloud1"></div>
+ <div class="cloud cloud2" id="cloud2"></div>
+ </div>
+
+ <div class="info">
+ <div class="city" id="city">Los Angeles</div>
+ <div class="temp" id="temp">72</div>
+ <div class="condition" id="condition">Sunny</div>
+ </div>
+
+ <button class="cycle-btn" onclick="cycleWeather()">Change Weather</button>
+ </div>
+
+ <script>
+ const weatherStates = [
+ { type: 'sunny', city: 'Los Angeles', temp: 72, condition: 'Sunny', bgClass: 'sunny-bg' },
+ { type: 'cloudy', city: 'London', temp: 58, condition: 'Cloudy', bgClass: 'cloudy-bg' },
+ { type: 'rainy', city: 'Seattle', temp: 52, condition: 'Rainy', bgClass: 'rainy-bg' },
+ { type: 'snowy', city: 'Denver', temp: 28, condition: 'Snowy', bgClass: 'snowy-bg' }
+ ];
+
+ let currentIndex = 0;
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ let animationId = null;
+ let particles = [];
+
+ function resizeCanvas() {
+ const card = document.querySelector('.weather-card');
+ canvas.width = card.offsetWidth;
+ canvas.height = card.offsetHeight;
+ }
+
+ window.addEventListener('resize', resizeCanvas);
+ resizeCanvas();
+
+ class RainDrop {
+ constructor() {
+ this.reset();
+ }
+
+ reset() {
+ this.x = Math.random() * canvas.width;
+ this.y = Math.random() * -canvas.height;
+ this.speed = 8 + Math.random() * 7;
+ this.length = 15 + Math.random() * 10;
+ this.opacity = 0.4 + Math.random() * 0.4;
+ this.width = 1 + Math.random();
+ }
+
+ update() {
+ this.y += this.speed;
+ if (this.y > canvas.height) {
+ this.reset();
+ }
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.strokeStyle = `rgba(174, 194, 224, ${this.opacity})`;
+ ctx.lineWidth = this.width;
+ ctx.moveTo(this.x, this.y);
+ ctx.lineTo(this.x - 1, this.y + this.length);
+ ctx.stroke();
+ }
+ }
+
+ class SnowFlake {
+ constructor() {
+ this.reset();
+ }
+
+ reset() {
+ this.x = Math.random() * canvas.width;
+ this.y = Math.random() * -canvas.height;
+ this.size = 2 + Math.random() * 3;
+ this.speed = 1 + Math.random() * 1.5;
+ this.drift = (Math.random() - 0.5) * 0.8;
+ this.opacity = 0.5 + Math.random() * 0.5;
+ this.swing = Math.random() * Math.PI * 2;
+ }
+
+ update() {
+ this.y += this.speed;
+ this.swing += 0.02;
+ this.x += this.drift + Math.sin(this.swing) * 0.5;
+
+ if (this.y > canvas.height) {
+ this.reset();
+ }
+ if (this.x > canvas.width) this.x = 0;
+ if (this.x < 0) this.x = canvas.width;
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.fillStyle = `rgba(255, 255, 255, ${this.opacity})`;
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ function initParticles(type) {
+ particles = [];
+ const count = type === 'rainy' ? 120 : 60;
+ for (let i = 0; i < count; i++) {
+ particles.push(type === 'rainy' ? new RainDrop() : new SnowFlake());
+ }
+ }
+
+ function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ const currentType = weatherStates[currentIndex].type;
+ if (currentType === 'rainy' || currentType === 'snowy') {
+ particles.forEach(p => {
+ p.update();
+ p.draw();
+ });
+ }
+
+ animationId = requestAnimationFrame(animate);
+ }
+
+ function updateWeather() {
+ const state = weatherStates[currentIndex];
+
+ document.getElementById('city').textContent = state.city;
+ document.getElementById('temp').textContent = state.temp;
+ document.getElementById('condition').textContent = state.condition;
+
+ document.body.className = state.bgClass;
+
+ document.getElementById('sun').classList.toggle('active', state.type === 'sunny');
+ document.getElementById('cloud1').classList.toggle('active', state.type === 'cloudy' || state.type === 'rainy');
+ document.getElementById('cloud2').classList.toggle('active', state.type === 'cloudy' || state.type === 'rainy');
+
+ if (state.type === 'rainy' || state.type === 'snowy') {
+ initParticles(state.type);
+ } else {
+ particles = [];
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ }
+ }
+
+ function cycleWeather() {
+ currentIndex = (currentIndex + 1) % weatherStates.length;
+ updateWeather();
+ }
+
+ updateWeather();
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/b14c4e0ccc4e/kimi.png b/data/artifacts/b14c4e0ccc4e/kimi.png
new file mode 100644
index 0000000..6c5e9c2
Binary files /dev/null and b/data/artifacts/b14c4e0ccc4e/kimi.png differ
diff --git a/data/artifacts/b14c4e0ccc4e/qwen25-7b.html b/data/artifacts/b14c4e0ccc4e/qwen25-7b.html
new file mode 100644
index 0000000..9849430
--- /dev/null
+++ b/data/artifacts/b14c4e0ccc4e/qwen25-7b.html
@@ -0,0 +1,169 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Animated Weather Card</title>
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ background-color: #f5f5f5;
+ }
+ .card {
+ width: 320px;
+ padding: 20px;
+ backdrop-filter: blur(10px);
+ border-radius: 16px;
+ background: linear-gradient(
+ 90deg,
+ rgba(255, 255, 255, 0.7) 0%,
+ rgba(255, 255, 255, 0.3) 100%
+ );
+ }
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+ }
+ .card-title {
+ font-size: 24px;
+ font-weight: bold;
+ }
+ .card-weather {
+ font-size: 32px;
+ color: #40a9ff;
+ }
+ .card-footer {
+ margin-top: auto;
+ }
+ .controls {
+ display: flex;
+ gap: 16px;
+ }
+ </style>
+</head>
+<body>
+ <div class="card">
+ <div class="card-header">
+ <span id="title">London</span>
+ <button onclick="cycleWeather()">Change Weather</button>
+ </div>
+ <div class="card-weather" id="weather"></div>
+ <canvas id="animation"></canvas>
+ <div class="card-footer">
+ <div class="controls">
+ <button onclick="drawSunrays()">Sun Rays</button>
+ <button onclick="drawClouds()">Drifting Clouds</button>
+ <button onclick="drawRain()">Rain</button>
+ <button onclick="drawSnow()">Snow</button>
+ </div>
+ </div>
+ </div>
+
+ <script>
+ const title = document.getElementById('title');
+ const weather = document.getElementById('weather');
+ let currentWeather = 0;
+
+ function cycleWeather() {
+ currentWeather = (currentWeather + 1) % 4;
+ updateWeather();
+ }
+
+ function updateWeather() {
+ switch (currentWeather) {
+ case 0:
+ title.textContent = 'London';
+ weather.textContent = '72°F';
+ break;
+ case 1:
+ title.textContent = 'Paris';
+ weather.textContent = '68°F';
+ break;
+ case 2:
+ title.textContent = 'Tokyo';
+ weather.textContent = '75°F';
+ break;
+ case 3:
+ title.textContent = 'New York';
+ weather.textContent = '59°F';
+ break;
+ }
+ }
+
+ function drawSunrays() {
+ const canvas = document.getElementById('animation');
+ if (!canvas) return;
+ canvas.width = canvas.height = 160;
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for (let i = 0; i < 30; i++) {
+ const angle = Math.PI * 2 * i / 30 + Math.random() * Math.PI / 18;
+ const x = 64 + 50 * Math.cos(angle);
+ const y = 64 - 50 * Math.sin(angle);
+ ctx.beginPath();
+ ctx.moveTo(80, 80);
+ ctx.lineTo(x, y);
+ ctx.stroke();
+ }
+ }
+
+ function drawClouds() {
+ const canvas = document.getElementById('animation');
+ if (!canvas) return;
+ canvas.width = canvas.height = 160;
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for (let i = 0; i < 30; i++) {
+ const x = Math.random() * 160 + 40;
+ const y = Math.random() * 80 + 20;
+ ctx.beginPath();
+ ctx.arc(x, y, 5 + Math.random() * 10, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ function drawRain() {
+ const canvas = document.getElementById('animation');
+ if (!canvas) return;
+ canvas.width = canvas.height = 160;
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for (let i = 0; i < 50; i++) {
+ const x = Math.random() * 160 + 40;
+ const y = -Math.random() * 80 - 20;
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x, y + 3);
+ ctx.stroke();
+ }
+ }
+
+ function drawSnow() {
+ const canvas = document.getElementById('animation');
+ if (!canvas) return;
+ canvas.width = canvas.height = 160;
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for (let i = 0; i < 50; i++) {
+ const x = Math.random() * 160 + 40;
+ const y = -Math.random() * 80 - 20;
+ ctx.beginPath();
+ ctx.arc(x, y, 2, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ updateWeather();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/b14c4e0ccc4e/qwen25-7b.png b/data/artifacts/b14c4e0ccc4e/qwen25-7b.png
new file mode 100644
index 0000000..04a2124
Binary files /dev/null and b/data/artifacts/b14c4e0ccc4e/qwen25-7b.png differ
diff --git a/data/artifacts/b14c4e0ccc4e/qwen3-14b.html b/data/artifacts/b14c4e0ccc4e/qwen3-14b.html
new file mode 100644
index 0000000..4bc0566
--- /dev/null
+++ b/data/artifacts/b14c4e0ccc4e/qwen3-14b.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Animated Weather Card</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ background: linear-gradient(to bottom right, #1f2937, #111827);
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ .weather-card {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(15px);
+ border-radius: 20px;
+ padding: 30px 40px;
+ width: 300px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ position: relative;
+ overflow: hidden;
+ }
+ .weather-card h1 {
+ margin: 0;
+ font-size: 2em;
+ color: #f8fafc;
+ }
+ .weather-card .temp {
+ font-size: 3em;
+ margin: 10px 0 20px;
+ color: #fbbf24;
+ }
+ .weather-card .condition {
+ font-size: 1.2em;
+ color: #d1d5db;
+ }
+ canvas {
+ position: absolute;
+ top: 0;
+ left: 0;
+ pointer-events: none;
+ }
+ button {
+ margin-top: 30px;
+ padding: 10px 20px;
+ font-size: 1em;
+ border: none;
+ border-radius: 10px;
+ background: rgba(255, 255, 255, 0.1);
+ color: #f8fafc;
+ cursor: pointer;
+ transition: 0.3s;
+ }
+ button:hover {
+ background: rgba(255, 255, 255, 0.2);
+ }
+ </style>
+</head>
+<body>
+ <div class="weather-card">
+ <h1>London</h1>
+ <div class="temp">22°C</div>
+ <div class="condition">Sunny</div>
+ <canvas id="weatherCanvas" width="300" height="300"></canvas>
+ <button onclick="cycleWeather()">Cycle Weather</button>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('weatherCanvas');
+ const ctx = canvas.getContext('2d');
+ let weatherIndex = 0;
+ const weatherStates = [
+ { name: 'Sunny', color: '#fbbf24', animate: drawSun },
+ { name: 'Cloudy', color: '#94a3b8', animate: drawClouds },
+ { name: 'Rainy', color: '#38bdf8', animate: drawRain },
+ { name: 'Snowy', color: '#f3f4f6', animate: drawSnow }
+ ];
+
+ function drawSun() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.fillStyle = weatherStates[weatherIndex].color;
+ ctx.beginPath();
+ ctx.arc(150, 150, 60, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.strokeStyle = '#fbbf24';
+ ctx.lineWidth = 4;
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.moveTo(150, 90);
+ ctx.lineTo(150, 30);
+ ctx.lineTo(110, 90);
+ ctx.lineTo(150, 90);
+ ctx.lineTo(190, 90);
+ ctx.lineTo(150, 30);
+ ctx.closePath();
+ ctx.fillStyle = '#fbbf24';
+ ctx.fill();
+ }
+
+ function drawClouds() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.fillStyle = '#ffffff';
+ ctx.beginPath();
+ ctx.arc(80, 100, 30, 0, Math.PI * 2);
+ ctx.arc(120, 100, 30, 0, Math.PI * 2);
+ ctx.arc(100, 120, 20, 0, Math.PI * 2);
+ ctx.arc(140, 120, 20, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ function drawRain() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.fillStyle = '#38bdf8';
+ for (let i = 0; i < 50; i++) {
+ ctx.beginPath();
+ ctx.moveTo(Math.random() * 300, Math.random() * 300);
+ ctx.lineTo(Math.random() * 300, Math.random() * 300 + 100);
+ ctx.stroke();
+ }
+ }
+
+ function drawSnow() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.fillStyle = '#f3f4f6';
+ for (let i = 0; i < 100; i++) {
+ ctx.beginPath();
+ ctx.arc(Math.random() * 300, Math.random() * 300, 3, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ function updateWeather() {
+ const state = weatherStates[weatherIndex];
+ document.querySelector('.condition').textContent = state.name;
+ document.querySelector('.temp').textContent = (Math.floor(Math.random() * 20) + 10) + '°C';
+ state.animate();
+ }
+
+ function cycleWeather() {
+ weatherIndex = (weatherIndex + 1) % 4;
+ updateWeather();
+ }
+
+ updateWeather();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/b14c4e0ccc4e/qwen3-14b.png b/data/artifacts/b14c4e0ccc4e/qwen3-14b.png
new file mode 100644
index 0000000..ad6c067
Binary files /dev/null and b/data/artifacts/b14c4e0ccc4e/qwen3-14b.png differ
diff --git a/data/artifacts/e85044630905/claude-code.html b/data/artifacts/e85044630905/claude-code.html
new file mode 100644
index 0000000..3dea215
--- /dev/null
+++ b/data/artifacts/e85044630905/claude-code.html
@@ -0,0 +1,244 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>Rubik's Cube</title>
+<style>
+ :root { --s: 60px; }
+ * { box-sizing: border-box; user-select: none; -webkit-user-select: none; }
+ html, body { height: 100%; }
+ body {
+ margin: 0; display: flex; flex-direction: column; align-items: center;
+ background: radial-gradient(circle at 50% 30%, #2b303c, #12141a 75%);
+ font-family: system-ui, -apple-system, sans-serif; color: #e8e9ef; overflow: hidden;
+ }
+ h1 { font-size: 16px; font-weight: 600; letter-spacing: 3px; text-transform: uppercase; margin: 14px 0 2px; opacity: .85; }
+ .hint { font-size: 12px; opacity: .5; margin: 0 0 4px; }
+ #viewport {
+ flex: 1; width: 100%; display: flex; align-items: center; justify-content: center;
+ perspective: 1000px; cursor: grab; touch-action: none;
+ }
+ #viewport.dragging { cursor: grabbing; }
+ #cube { position: relative; width: 0; height: 0; transform-style: preserve-3d; }
+ .cubie {
+ position: absolute; width: var(--s); height: var(--s);
+ left: calc(var(--s) / -2); top: calc(var(--s) / -2);
+ transform-style: preserve-3d;
+ }
+ .face {
+ position: absolute; inset: 0; border-radius: 14%;
+ border: 3px solid #0b0d11;
+ box-shadow: inset 0 0 6px rgba(0,0,0,.35);
+ }
+ .f-front { transform: translateZ(calc(var(--s) / 2)); }
+ .f-back { transform: rotateY(180deg) translateZ(calc(var(--s) / 2)); }
+ .f-right { transform: rotateY(90deg) translateZ(calc(var(--s) / 2)); }
+ .f-left { transform: rotateY(-90deg) translateZ(calc(var(--s) / 2)); }
+ .f-top { transform: rotateX(90deg) translateZ(calc(var(--s) / 2)); }
+ .f-bottom { transform: rotateX(-90deg) translateZ(calc(var(--s) / 2)); }
+ #controls { padding: 10px 12px 16px; display: flex; flex-direction: column; gap: 8px; align-items: center; }
+ .row { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
+ button {
+ font: 600 14px/1 system-ui, sans-serif; color: #e8e9ef;
+ background: linear-gradient(180deg, #3a4152, #2a3040);
+ border: 1px solid #4a5268; border-radius: 9px;
+ min-width: 46px; padding: 10px 8px; cursor: pointer;
+ transition: transform .08s, background .15s;
+ }
+ button:hover { background: linear-gradient(180deg, #4a5268, #333a4c); }
+ button:active { transform: scale(.93); }
+ .big { min-width: 100px; letter-spacing: 1px; }
+ #scramble { background: linear-gradient(180deg, #7c5cff, #5b3fe0); border-color: #8f78ff; }
+ #scramble:hover { background: linear-gradient(180deg, #8d70ff, #6a4ef0); }
+ #reset { background: linear-gradient(180deg, #2f8f5b, #1f6f44); border-color: #3fae72; }
+ #reset:hover { background: linear-gradient(180deg, #37a066, #27804f); }
+</style>
+</head>
+<body>
+<h1>Rubik's Cube</h1>
+<p class="hint">Drag to orbit · buttons turn faces · keys U/D/L/R/F/B (Shift = counter-clockwise)</p>
+<div id="viewport"><div id="cube"></div></div>
+<div id="controls">
+ <div class="row" id="movesRow"></div>
+ <div class="row" id="primesRow"></div>
+ <div class="row">
+ <button id="scramble" class="big">Scramble</button>
+ <button id="reset" class="big">Reset</button>
+ </div>
+</div>
+<script>
+(function () {
+ var viewport = document.getElementById('viewport');
+ var cubeEl = document.getElementById('cube');
+
+ var S = Math.max(34, Math.min(72, Math.floor(Math.min(innerWidth, innerHeight - 200) / 5.2)));
+ document.documentElement.style.setProperty('--s', S + 'px');
+
+ var DARK = '#171a20';
+ var I3 = [1,0,0, 0,1,0, 0,0,1];
+
+ function mul(a, b) { // 3x3 row-major
+ var r = new Array(9);
+ for (var i = 0; i < 3; i++) for (var j = 0; j < 3; j++)
+ r[i*3+j] = a[i*3]*b[j] + a[i*3+1]*b[3+j] + a[i*3+2]*b[6+j];
+ return r;
+ }
+ function applyM(m, v) {
+ return [ m[0]*v[0]+m[1]*v[1]+m[2]*v[2],
+ m[3]*v[0]+m[4]*v[1]+m[5]*v[2],
+ m[6]*v[0]+m[7]*v[1]+m[8]*v[2] ];
+ }
+ function roundM(m) { return m.map(function (v) { return Math.round(v); }); }
+ function axisRot(axis, deg) {
+ var r = deg * Math.PI / 180, c = Math.cos(r), s = Math.sin(r);
+ if (axis === 'x') return [1,0,0, 0,c,-s, 0,s,c];
+ if (axis === 'y') return [c,0,s, 0,1,0, -s,0,c];
+ return [c,-s,0, s,c,0, 0,0,1];
+ }
+
+ var cubies = [];
+ function makeFace(cls, color) {
+ var f = document.createElement('div');
+ f.className = 'face ' + cls;
+ f.style.background = color === DARK ? DARK
+ : 'linear-gradient(145deg, ' + color + ', ' + color + ' 60%, rgba(0,0,0,.18))';
+ if (color !== DARK) f.style.backgroundColor = color;
+ return f;
+ }
+ function build() {
+ cubeEl.innerHTML = '';
+ cubies = [];
+ for (var x = -1; x <= 1; x++) for (var y = -1; y <= 1; y++) for (var z = -1; z <= 1; z++) {
+ if (!x && !y && !z) continue;
+ var el = document.createElement('div');
+ el.className = 'cubie';
+ el.appendChild(makeFace('f-top', y === -1 ? '#f4f6f5' : DARK));
+ el.appendChild(makeFace('f-bottom', y === 1 ? '#ffd60a' : DARK));
+ el.appendChild(makeFace('f-right', x === 1 ? '#e5484d' : DARK));
+ el.appendChild(makeFace('f-left', x === -1 ? '#ff9f1c' : DARK));
+ el.appendChild(makeFace('f-front', z === 1 ? '#30c04f' : DARK));
+ el.appendChild(makeFace('f-back', z === -1 ? '#3b82f6' : DARK));
+ cubeEl.appendChild(el);
+ var c = { x: x, y: y, z: z, ix: x, iy: y, iz: z, m: I3.slice(), el: el };
+ cubies.push(c);
+ render(c, null);
+ }
+ }
+ function render(c, R) {
+ var m = R ? mul(R, c.m) : c.m;
+ var p = [c.x * S, c.y * S, c.z * S];
+ if (R) p = applyM(R, p);
+ c.el.style.transform = 'matrix3d(' +
+ m[0] + ',' + m[3] + ',' + m[6] + ',0,' +
+ m[1] + ',' + m[4] + ',' + m[7] + ',0,' +
+ m[2] + ',' + m[5] + ',' + m[8] + ',0,' +
+ p[0] + ',' + p[1] + ',' + p[2] + ',1)';
+ }
+
+ // Face turns: clockwise viewed from outside = +90deg about the outward normal.
+ var MOVES = {
+ U: { axis: 'y', layer: -1, dir: -1 },
+ D: { axis: 'y', layer: 1, dir: 1 },
+ R: { axis: 'x', layer: 1, dir: 1 },
+ L: { axis: 'x', layer: -1, dir: -1 },
+ F: { axis: 'z', layer: 1, dir: 1 },
+ B: { axis: 'z', layer: -1, dir: -1 }
+ };
+
+ var queue = [], animating = false, gen = 0;
+ function coordOf(c, axis) { return axis === 'x' ? c.x : axis === 'y' ? c.y : c.z; }
+ function enqueue(name, prime, dur) {
+ var mv = MOVES[name];
+ queue.push({ axis: mv.axis, layer: mv.layer, dir: mv.dir * (prime ? -1 : 1), dur: dur || 260 });
+ if (!animating) next();
+ }
+ function next() {
+ var item = queue.shift();
+ if (!item) { animating = false; return; }
+ animating = true;
+ var myGen = gen;
+ var affected = cubies.filter(function (c) { return coordOf(c, item.axis) === item.layer; });
+ var t0 = performance.now();
+ function frame(now) {
+ if (myGen !== gen) return;
+ var t = Math.min(1, (now - t0) / item.dur);
+ var e = t < .5 ? 2*t*t : 1 - Math.pow(-2*t + 2, 2) / 2;
+ var R = axisRot(item.axis, 90 * item.dir * e);
+ affected.forEach(function (c) { render(c, R); });
+ if (t < 1) { requestAnimationFrame(frame); return; }
+ var Rq = roundM(axisRot(item.axis, 90 * item.dir));
+ affected.forEach(function (c) {
+ var p = applyM(Rq, [c.x, c.y, c.z]).map(Math.round);
+ c.x = p[0]; c.y = p[1]; c.z = p[2];
+ c.m = roundM(mul(Rq, c.m));
+ render(c, null);
+ });
+ next();
+ }
+ requestAnimationFrame(frame);
+ }
+
+ function scramble() {
+ var names = Object.keys(MOVES), last = '';
+ for (var i = 0; i < 20; i++) {
+ var n;
+ do { n = names[Math.floor(Math.random() * names.length)]; } while (n === last);
+ last = n;
+ enqueue(n, Math.random() < .5, 110);
+ }
+ }
+ function reset() {
+ gen++; queue.length = 0; animating = false;
+ cubies.forEach(function (c) {
+ c.x = c.ix; c.y = c.iy; c.z = c.iz; c.m = I3.slice();
+ render(c, null);
+ });
+ }
+
+ // Buttons
+ var movesRow = document.getElementById('movesRow');
+ var primesRow = document.getElementById('primesRow');
+ ['U','D','L','R','F','B'].forEach(function (n) {
+ var b = document.createElement('button');
+ b.textContent = n;
+ b.onclick = function () { enqueue(n, false); };
+ movesRow.appendChild(b);
+ var bp = document.createElement('button');
+ bp.textContent = n + '\u2032';
+ bp.onclick = function () { enqueue(n, true); };
+ primesRow.appendChild(bp);
+ });
+ document.getElementById('scramble').onclick = scramble;
+ document.getElementById('reset').onclick = reset;
+ document.addEventListener('keydown', function (ev) {
+ var k = ev.key.toUpperCase();
+ if (MOVES[k] && !ev.metaKey && !ev.ctrlKey) enqueue(k, ev.shiftKey);
+ });
+
+ // Orbit
+ var rx = -28, ry = -38, dragging = false, px = 0, py = 0;
+ function applyOrbit() { cubeEl.style.transform = 'rotateX(' + rx + 'deg) rotateY(' + ry + 'deg)'; }
+ applyOrbit();
+ viewport.addEventListener('pointerdown', function (ev) {
+ dragging = true; px = ev.clientX; py = ev.clientY;
+ viewport.classList.add('dragging');
+ viewport.setPointerCapture(ev.pointerId);
+ });
+ viewport.addEventListener('pointermove', function (ev) {
+ if (!dragging) return;
+ ry += (ev.clientX - px) * 0.4;
+ rx -= (ev.clientY - py) * 0.4;
+ rx = Math.max(-90, Math.min(90, rx));
+ px = ev.clientX; py = ev.clientY;
+ applyOrbit();
+ });
+ function endDrag() { dragging = false; viewport.classList.remove('dragging'); }
+ viewport.addEventListener('pointerup', endDrag);
+ viewport.addEventListener('pointercancel', endDrag);
+
+ build();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e85044630905/claude-code.png b/data/artifacts/e85044630905/claude-code.png
new file mode 100644
index 0000000..f727db5
Binary files /dev/null and b/data/artifacts/e85044630905/claude-code.png differ
diff --git a/data/artifacts/e85044630905/gemma3-12b.html b/data/artifacts/e85044630905/gemma3-12b.html
new file mode 100644
index 0000000..511f785
--- /dev/null
+++ b/data/artifacts/e85044630905/gemma3-12b.html
@@ -0,0 +1,216 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>3D Rubik's Cube</title>
+<style>
+body { margin: 0; overflow: hidden; }
+
+#container {
+ width: 400px;
+ height: 400px;
+ position: relative;
+ margin: auto;
+ transform-style: preserve-3d;
+}
+
+.cube {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ transform-style: preserve-3d;
+}
+
+.face {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 1;
+ transform-style: preserve-3d;
+}
+
+.face div {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ display: block;
+}
+
+#controls {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ transform: translateX(-50%);
+ padding: 10px;
+ background-color: rgba(255, 255, 255, 0.7);
+ border-radius: 5px;
+}
+
+button { margin: 5px; }
+
+</style>
+</head>
+<body>
+
+<div id="container"></div>
+
+<div id="controls">
+ <button onclick="rotateFace('U')">Up</button>
+ <button onclick="rotateFace('D')">Down</button>
+ <button onclick="rotateFace('L')">Left</button>
+ <button onclick="rotateFace('R')">Right</button>
+ <button onclick="rotateFace('F')">Front</button>
+ <button onclick="rotateFace('B')">Back</button>
+ <button onclick="scramble()">Scramble</button>
+ <button onclick="resetCube()">Reset</button>
+</div>
+
+<script>
+const container = document.getElementById('container');
+let cubeState = [
+ [0, 1, 2], [3, 4, 5], [6, 7, 8],
+ [9, 10, 11], [12, 13, 14], [15, 16, 17],
+ [18, 19, 20], [21, 22, 23], [24, 25, 26]
+];
+
+let rotationX = 0;
+let rotationY = 0;
+const rotationSpeed = 0.02;
+
+function createCube() {
+ for (let i = 0; i < 27; i++) {
+ const cubePiece = document.createElement('div');
+ cubePiece.classList.add('cube');
+ container.appendChild(cubePiece);
+
+ const face = document.createElement('div');
+ face.classList.add('face');
+ cubePiece.appendChild(face);
+
+ const colorDiv = document.createElement('div');
+ colorDiv.style.backgroundColor = getColor(i);
+ face.appendChild(colorDiv);
+ }
+
+ const cubes = container.querySelectorAll('.cube');
+ for (let i = 0; i < 27; i++) {
+ const cubePiece = cubes[i];
+ const [x, y, z] = cubeState[i];
+ cubePiece.style.transform = `translate(${x * 100}px, ${y * 100}px, ${z * 100}px) rotateX(0deg) rotateY(0deg)`;
+
+ }
+
+}
+
+
+function getColor(index) {
+ const colors = [
+ '#FFFFFF', '#FF0000', '#00FF00', '#0000FF', // White, Red, Green, Blue
+ '#FFFF00', '#FFA500', '#800080', '#4682B4' // Yellow, Orange, Purple, SteelBlue
+ ];
+
+ const face = Math.floor(index / 9);
+ return colors[face % colors.length]; // Cycle through the colours for each face
+}
+
+
+function rotateFace(face) {
+ let rotationAngle = 90;
+ if (face === 'U') {
+ cubeState.forEach(row => [0, 1, 2].includes(row[2]) ? row[2] = -row[2] : 0);
+ } else if (face === 'D') {
+ cubeState.forEach(row => [6, 7, 8].includes(row[2]) ? row[2] = -row[2] : 0);
+ } else if (face === 'L') {
+ cubeState.forEach(row => [0, 6, 18, 24].includes(row[0]) ? row[0] = -row[0] : 0);
+ } else if (face === 'R') {
+ cubeState.forEach(row => [2, 8, 20, 26].includes(row[0]) ? row[0] = -row[0] : 0);
+ } else if (face === 'F') {
+ cubeState.forEach(row => [9, 12, 15, 18, 21, 24].includes(row[1]) ? row[1] = -row[1] : 0);
+
+ } else if (face === 'B') {
+ cubeState.forEach(row => [3, 5, 11, 17, 22, 25].includes(row[1]) ? row[1] = -row[1] : 0);
+ }
+
+ updateCubePositions();
+}
+
+
+
+function scramble() {
+ for (let i = 0; i < 10; i++) {
+ const face = ['U', 'D', 'L', 'R', 'F', 'B'][Math.floor(Math.random() * 6)];
+ rotateFace(face);
+ }
+}
+
+function resetCube() {
+ cubeState = [
+ [0, 1, 2], [3, 4, 5], [6, 7, 8],
+ [9, 10, 11], [12, 13, 14], [15, 16, 17],
+ [18, 19, 20], [21, 22, 23], [24, 25, 26]
+ ];
+
+ updateCubePositions();
+}
+
+function updateCubePositions() {
+ const cubes = container.querySelectorAll('.cube');
+ for (let i = 0; i < 27; i++) {
+ const cubePiece = cubes[i];
+ const [x, y, z] = cubeState[i];
+ cubePiece.style.transform = `translate(${x * 100}px, ${y * 100}px, ${z * 100}px) rotateX(0deg) rotateY(0deg)`;
+ }
+}
+
+
+createCube();
+
+document.addEventListener('mousemove', (e) => {
+ rotationY += (e.clientX - container.offsetWidth / 2) * rotationSpeed;
+ //rotationX -= (e.clientY - container.offsetHeight / 2) * rotationSpeed; // Optional: Tilt the cube
+});
+
+
+let mouseDown = false;
+
+document.addEventListener('mousedown', () => {
+ mouseDown = true;
+});
+
+document.addEventListener('mouseup', () => {
+ mouseDown = false;
+});
+
+document.addEventListener('mouseleave', () => {
+ mouseDown = false;
+});
+
+
+let prevX = 0, prevY = 0;
+
+document.addEventListener('mousemove', (e) => {
+ if (!mouseDown) return;
+
+ const dx = e.clientX - prevX;
+ const dy = e.clientY - prevY;
+
+ rotationY += dx * rotationSpeed;
+ //rotationX -= dy * rotationSpeed; // Optional: Tilt the cube
+
+ prevX = e.clientX;
+ prevY = e.clientY;
+
+
+});
+
+
+
+function animate() {
+ container.style.transform = `rotateY(${rotationY}deg)`;
+ requestAnimationFrame(animate);
+}
+
+animate();
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e85044630905/gemma3-12b.png b/data/artifacts/e85044630905/gemma3-12b.png
new file mode 100644
index 0000000..9b14cfc
Binary files /dev/null and b/data/artifacts/e85044630905/gemma3-12b.png differ
diff --git a/data/artifacts/e85044630905/gpt.html b/data/artifacts/e85044630905/gpt.html
new file mode 100644
index 0000000..1347cf2
--- /dev/null
+++ b/data/artifacts/e85044630905/gpt.html
@@ -0,0 +1,377 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>3D Rubik's Cube</title>
+<style>
+html,body{
+ margin:0;
+ padding:0;
+ height:100%;
+ overflow:hidden;
+ background:#111;
+ color:#eee;
+ font-family:system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+}
+#app{
+ display:flex;
+ flex-direction:column;
+ align-items:center;
+ justify-content:center;
+ height:100%;
+}
+#scene{
+ width:420px;
+ height:420px;
+ perspective:800px;
+ cursor:grab;
+}
+#cube-wrapper{
+ width:100%;
+ height:100%;
+ position:relative;
+}
+#cube{
+ position:absolute;
+ top:50%;
+ left:50%;
+ width:180px;
+ height:180px;
+ transform-style:preserve-3d;
+ transform:translate3d(-50%,-50%,0) rotateX(-30deg) rotateY(-30deg);
+ transition:transform 0.1s linear;
+}
+.cubie{
+ position:absolute;
+ width:58px;
+ height:58px;
+ transform-style:preserve-3d;
+}
+.face{
+ position:absolute;
+ width:58px;
+ height:58px;
+ box-sizing:border-box;
+ border:2px solid #000;
+ backface-visibility:visible;
+}
+/* Face directions */
+.face-U{background:#fff; transform:translateZ(29px);}
+.face-D{background:#ffd500; transform:rotateX(180deg) translateZ(29px);}
+.face-F{background:#009b48; transform:rotateX(90deg) translateZ(29px);}
+.face-B{background:#0046ad; transform:rotateX(-90deg) translateZ(29px);}
+.face-R{background:#b71234; transform:rotateY(90deg) translateZ(29px);}
+.face-L{background:#ff5800; transform:rotateY(-90deg) translateZ(29px);}
+/* Control panel */
+#controls{
+ margin-top:8px;
+ display:flex;
+ flex-wrap:wrap;
+ gap:4px;
+ justify-content:center;
+ font-size:12px;
+}
+#controls button{
+ background:#222;
+ color:#eee;
+ border:1px solid #555;
+ padding:4px 6px;
+ cursor:pointer;
+}
+#controls button:hover{
+ background:#333;
+}
+#status{
+ margin-top:4px;
+ font-size:11px;
+ color:#bbb;
+ text-align:center;
+ max-width:420px;
+}
+</style>
+</head>
+<body>
+<div id="app">
+ <div id="scene">
+ <div id="cube-wrapper">
+ <div id="cube"></div>
+ </div>
+ </div>
+ <div id="controls">
+ <button data-move="U">U</button>
+ <button data-move="U'">U'</button>
+ <button data-move="D">D</button>
+ <button data-move="D'">D'</button>
+ <button data-move="L">L</button>
+ <button data-move="L'">L'</button>
+ <button data-move="R">R</button>
+ <button data-move="R'">R'</button>
+ <button data-move="F">F</button>
+ <button data-move="F'">F'</button>
+ <button data-move="B">B</button>
+ <button data-move="B'">B'</button>
+ <button id="scramble">Scramble</button>
+ <button id="reset">Reset</button>
+ </div>
+ <div id="status">Drag to rotate the cube. Use buttons for face turns. No solving logic: this is a visual, animating model.</div>
+</div>
+
+<script>
+(function(){
+ const cubeEl = document.getElementById('cube');
+ const sceneEl = document.getElementById('scene');
+ const buttons = document.querySelectorAll('#controls button[data-move]');
+ const scrambleBtn = document.getElementById('scramble');
+ const resetBtn = document.getElementById('reset');
+
+ const size = 3;
+ const spacing = 60; // cubie size + gap
+ const offset = -spacing; // center for -1,0,1
+ const cubies = [];
+ const cubieState = {}; // id -> {x,y,z,rot}
+
+ // Initialize cube pieces
+ let idCounter = 0;
+ for(let x=-1;x<=1;x++){
+ for(let y=-1;y<=1;y++){
+ for(let z=-1;z<=1;z++){
+ if(x===0 && y===0 && z===0) continue;
+ const cubie = document.createElement('div');
+ cubie.className = 'cubie';
+ const id = 'c'+(idCounter++);
+ cubie.dataset.id = id;
+
+ // faces
+ if(y===1){ const f=document.createElement('div'); f.className='face face-U'; cubie.appendChild(f);}
+ if(y===-1){ const f=document.createElement('div'); f.className='face face-D'; cubie.appendChild(f);}
+ if(z===1){ const f=document.createElement('div'); f.className='face face-F'; cubie.appendChild(f);}
+ if(z===-1){ const f=document.createElement('div'); f.className='face face-B'; cubie.appendChild(f);}
+ if(x===1){ const f=document.createElement('div'); f.className='face face-R'; cubie.appendChild(f);}
+ if(x===-1){ const f=document.createElement('div'); f.className='face face-L'; cubie.appendChild(f);}
+
+ cubeEl.appendChild(cubie);
+ cubies.push(cubie);
+ cubieState[id] = {
+ x, y, z,
+ rot: {x:0,y:0,z:0} // orientation in 90° steps
+ };
+ positionCubie(id);
+ }
+ }
+ }
+
+ function positionCubie(id){
+ const s = cubieState[id];
+ const el = cubeEl.querySelector('[data-id="'+id+'"]');
+ const tx = (s.x*spacing)+offset+30;
+ const ty = (-s.y*spacing)+offset+30;
+ const tz = (s.z*spacing);
+ const rx = s.rot.x*90;
+ const ry = s.rot.y*90;
+ const rz = s.rot.z*90;
+ el.style.transform = `translate3d(${tx}px,${ty}px,${tz}px) rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`;
+ }
+
+ function applyRotationToCoords(axis, layer, dir){
+ // dir: +1 quarter turn, -1 counter
+ const eps = 0.1;
+ for(const id in cubieState){
+ const s = cubieState[id];
+ if(Math.abs(s[axis]-layer) > eps) continue;
+ let {x,y,z} = s;
+ let nx=x,ny=y,nz=z;
+ if(axis==='x'){
+ // rotate around x: (y,z)->( -dir*z, dir*y )
+ ny = -dir*z;
+ nz = dir*y;
+ }else if(axis==='y'){
+ // (x,z)->( dir*z, -dir*x )
+ nx = dir*z;
+ nz = -dir*x;
+ }else if(axis==='z'){
+ // (x,y)->( -dir*y, dir*x )
+ nx = -dir*y;
+ ny = dir*x;
+ }
+ s.x=nx;s.y=ny;s.z=nz;
+ }
+ }
+
+ function applyRotationToOrientation(axis, layer, dir){
+ const eps = 0.1;
+ for(const id in cubieState){
+ const s = cubieState[id];
+ if(Math.abs(s[axis]-layer) > eps) continue;
+ const r = s.rot;
+ // approximate orientation: rotate local axes
+ if(axis==='x'){
+ // y<->z
+ const oldY=r.y, oldZ=r.z;
+ if(dir===1){
+ r.y = (oldY+0)%4;
+ r.z = (oldZ+0)%4;
+ }
+ // more accurate: track orientation by rotating around x:
+ // (y,z)->(y-? , z+?)
+ // For visual purposes small inaccuracies are ok; we apply:
+ const t=r.y;
+ r.y = (r.y+dir+4)%4;
+ r.z = (r.z+dir+4)%4;
+ }else if(axis==='y'){
+ const t=r.x;
+ r.x = (r.x+dir+4)%4;
+ r.z = (r.z+dir+4)%4;
+ }else if(axis==='z'){
+ const t=r.x;
+ r.x = (r.x+dir+4)%4;
+ r.y = (r.y+dir+4)%4;
+ }
+ }
+ }
+
+ let animating = false;
+ const moveQueue = [];
+
+ function queueMove(move){
+ moveQueue.push(move);
+ if(!animating) processQueue();
+ }
+
+ function processQueue(){
+ if(animating) return;
+ const move = moveQueue.shift();
+ if(!move){ return; }
+ doMove(move, ()=>processQueue());
+ }
+
+ function doMove(notation, cb){
+ const prime = notation.includes("'");
+ const face = notation[0];
+ let axis, layer, dir;
+ switch(face){
+ case 'U': axis='y'; layer=1; dir=1; break;
+ case 'D': axis='y'; layer=-1; dir=-1; break;
+ case 'L': axis='x'; layer=-1; dir=-1; break;
+ case 'R': axis='x'; layer=1; dir=1; break;
+ case 'F': axis='z'; layer=1; dir=1; break;
+ case 'B': axis='z'; layer=-1; dir=-1; break;
+ }
+ if(prime) dir*=-1;
+
+ const layerCubies = [];
+ const eps=0.1;
+ for(const id in cubieState){
+ if(Math.abs(cubieState[id][axis]-layer)<=eps){
+ layerCubies.push(cubeEl.querySelector('[data-id="'+id+'"]'));
+ }
+ }
+
+ // animate: wrap in a group
+ const group = document.createElement('div');
+ group.style.position='absolute';
+ group.style.left='0';
+ group.style.top='0';
+ group.style.width='100%';
+ group.style.height='100%';
+ group.style.transformStyle='preserve-3d';
+ cubeEl.appendChild(group);
+ layerCubies.forEach(el=>group.appendChild(el));
+
+ animating=true;
+ const angle = dir*90;
+ const axisCss = axis.toUpperCase();
+ group.style.transition='transform 0.2s ease-out';
+ group.style.transform=`rotate${axisCss}(${angle}deg)`;
+
+ group.addEventListener('transitionend', function handler(){
+ group.removeEventListener('transitionend', handler);
+ // apply logical transform
+ applyRotationToCoords(axis, layer, dir);
+ applyRotationToOrientation(axis, layer, dir);
+ // ungroup
+ layerCubies.forEach(el=>{
+ cubeEl.appendChild(el);
+ const id = el.dataset.id;
+ positionCubie(id);
+ });
+ cubeEl.removeChild(group);
+ animating=false;
+ if(cb) cb();
+ }, {once:true});
+ }
+
+ buttons.forEach(btn=>{
+ btn.addEventListener('click', ()=>{
+ if(animating) return;
+ const m = btn.dataset.move;
+ queueMove(m);
+ });
+ });
+
+ scrambleBtn.addEventListener('click', ()=>{
+ if(animating) return;
+ const moves = ['U','U\'','D','D\'','L','L\'','R','R\'','F','F\'','B','B\''];
+ for(let i=0;i<25;i++){
+ const m=moves[Math.floor(Math.random()*moves.length)];
+ queueMove(m);
+ }
+ });
+
+ resetBtn.addEventListener('click', ()=>{
+ if(animating) return;
+ moveQueue.length=0;
+ for(const id in cubieState){
+ const idx = parseInt(id.slice(1),10);
+ }
+ // reset coordinates and orientation
+ let idx=0;
+ for(let x=-1;x<=1;x++){
+ for(let y=-1;y<=1;y++){
+ for(let z=-1;z<=1;z++){
+ if(x===0&&y===0&&z===0) continue;
+ const id='c'+(idx++);
+ cubieState[id].x=x;
+ cubieState[id].y=y;
+ cubieState[id].z=z;
+ cubieState[id].rot={x:0,y:0,z:0};
+ positionCubie(id);
+ }
+ }
+ }
+ });
+
+ // Camera orbit controls
+ let dragging=false;
+ let lastX=0,lastY=0;
+ let rotX=-30, rotY=-30;
+
+ function updateCubeRotation(){
+ cubeEl.style.transform=`translate3d(-50%,-50%,0) rotateX(${rotX}deg) rotateY(${rotY}deg)`;
+ }
+
+ sceneEl.addEventListener('mousedown',e=>{
+ dragging=true;
+ sceneEl.style.cursor='grabbing';
+ lastX=e.clientX;
+ lastY=e.clientY;
+ });
+ window.addEventListener('mouseup',()=>{
+ dragging=false;
+ sceneEl.style.cursor='grab';
+ });
+ window.addEventListener('mousemove',e=>{
+ if(!dragging) return;
+ const dx=e.clientX-lastX;
+ const dy=e.clientY-lastY;
+ lastX=e.clientX;
+ lastY=e.clientY;
+ rotY += dx*0.5;
+ rotX -= dy*0.5;
+ rotX = Math.max(-85, Math.min(85,rotX));
+ updateCubeRotation();
+ });
+
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e85044630905/gpt.png b/data/artifacts/e85044630905/gpt.png
new file mode 100644
index 0000000..c92c50b
Binary files /dev/null and b/data/artifacts/e85044630905/gpt.png differ
diff --git a/data/artifacts/e85044630905/grok.html b/data/artifacts/e85044630905/grok.html
new file mode 100644
index 0000000..de1f3fc
--- /dev/null
+++ b/data/artifacts/e85044630905/grok.html
@@ -0,0 +1,410 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>3D Rubik's Cube</title>
+<style>
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+ body {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(145deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
+ font-family: system-ui, -apple-system, sans-serif;
+ color: #eee;
+ overflow: hidden;
+ user-select: none;
+ }
+ h1 {
+ font-size: 1.4rem;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ margin-bottom: 0.4rem;
+ opacity: 0.9;
+ }
+ .hint {
+ font-size: 0.75rem;
+ opacity: 0.5;
+ margin-bottom: 0.8rem;
+ }
+ .stage-wrap {
+ width: min(92vw, 420px);
+ height: min(92vw, 420px);
+ perspective: 900px;
+ cursor: grab;
+ }
+ .stage-wrap:active { cursor: grabbing; }
+ .scene {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ transform-style: preserve-3d;
+ }
+ .cube-root {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ width: 0;
+ height: 0;
+ transform-style: preserve-3d;
+ }
+ .cubie {
+ position: absolute;
+ width: var(--size);
+ height: var(--size);
+ left: calc(var(--size) / -2);
+ top: calc(var(--size) / -2);
+ transform-style: preserve-3d;
+ transition: transform 0.28s ease-in-out;
+ }
+ .sticker {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ border-radius: 10%;
+ backface-visibility: hidden;
+ border: 2px solid #111;
+ box-shadow: inset 0 0 12px rgba(0,0,0,0.25);
+ }
+ .sticker.U { background: #ffffff; }
+ .sticker.D { background: #ffd500; }
+ .sticker.F { background: #009b48; }
+ .sticker.B { background: #0045ad; }
+ .sticker.L { background: #ff5800; }
+ .sticker.R { background: #b71234; }
+ .sticker.inner { background: #222; }
+ .controls {
+ margin-top: 1rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.55rem;
+ align-items: center;
+ }
+ .row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.4rem;
+ justify-content: center;
+ }
+ button {
+ background: rgba(255,255,255,0.08);
+ border: 1px solid rgba(255,255,255,0.18);
+ color: #eee;
+ padding: 0.45rem 0.75rem;
+ border-radius: 8px;
+ font-size: 0.8rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background 0.15s, transform 0.1s;
+ min-width: 2.6rem;
+ }
+ button:hover { background: rgba(255,255,255,0.16); }
+ button:active { transform: scale(0.96); }
+ button:disabled { opacity: 0.4; cursor: not-allowed; }
+ button.accent {
+ background: rgba(0,155,72,0.35);
+ border-color: rgba(0,155,72,0.5);
+ }
+ button.danger {
+ background: rgba(183,18,52,0.35);
+ border-color: rgba(183,18,52,0.5);
+ }
+ .label {
+ font-size: 0.7rem;
+ opacity: 0.55;
+ width: 100%;
+ text-align: center;
+ margin-bottom: -0.15rem;
+ }
+</style>
+</head>
+<body>
+ <h1>RUBIK'S CUBE</h1>
+ <p class="hint">Drag to orbit · Buttons turn faces</p>
+ <div class="stage-wrap" id="stage">
+ <div class="scene" id="scene">
+ <div class="cube-root" id="cubeRoot"></div>
+ </div>
+ </div>
+ <div class="controls">
+ <div class="label">Face turns (clockwise)</div>
+ <div class="row" id="faceBtns"></div>
+ <div class="label">Counter-clockwise</div>
+ <div class="row" id="faceBtnsCCW"></div>
+ <div class="row" style="margin-top:0.3rem">
+ <button class="accent" id="scrambleBtn">Scramble</button>
+ <button class="danger" id="resetBtn">Reset</button>
+ </div>
+ </div>
+<script>
+(function () {
+ const SIZE = 56;
+ const GAP = 2;
+ const STEP = SIZE + GAP;
+ const FACES = ['U', 'D', 'F', 'B', 'L', 'R'];
+ const FACE_COLORS = { U: 'U', D: 'D', F: 'F', B: 'B', L: 'L', R: 'R' };
+
+ // Axis normals for each face
+ const FACE_AXIS = {
+ U: [0, 1, 0], D: [0, -1, 0],
+ F: [0, 0, 1], B: [0, 0, -1],
+ L: [-1, 0, 0], R: [1, 0, 0]
+ };
+
+ // Rotation axis for face turns (right-hand rule, CW looking at face)
+ const TURN_AXIS = {
+ U: [0, -1, 0], D: [0, 1, 0],
+ F: [0, 0, -1], B: [0, 0, 1],
+ L: [1, 0, 0], R: [-1, 0, 0]
+ };
+
+ let cubies = [];
+ let animating = false;
+ let rotX = -28;
+ let rotY = 35;
+ const cubeRoot = document.getElementById('cubeRoot');
+ const scene = document.getElementById('scene');
+ const stage = document.getElementById('stage');
+
+ function applyCamera() {
+ scene.style.transform = `rotateX(${rotX}deg) rotateY(${rotY}deg)`;
+ }
+ applyCamera();
+
+ // Drag orbit
+ let dragging = false, lastX = 0, lastY = 0;
+ stage.addEventListener('pointerdown', e => {
+ dragging = true;
+ lastX = e.clientX;
+ lastY = e.clientY;
+ stage.setPointerCapture(e.pointerId);
+ });
+ stage.addEventListener('pointermove', e => {
+ if (!dragging) return;
+ const dx = e.clientX - lastX;
+ const dy = e.clientY - lastY;
+ lastX = e.clientX;
+ lastY = e.clientY;
+ rotY += dx * 0.5;
+ rotX -= dy * 0.5;
+ rotX = Math.max(-90, Math.min(90, rotX));
+ applyCamera();
+ });
+ stage.addEventListener('pointerup', () => { dragging = false; });
+ stage.addEventListener('pointercancel', () => { dragging = false; });
+
+ function stickerTransforms(s) {
+ const h = s / 2;
+ return {
+ U: `rotateX(90deg) translateZ(${h}px)`,
+ D: `rotateX(-90deg) translateZ(${h}px)`,
+ F: `translateZ(${h}px)`,
+ B: `rotateY(180deg) translateZ(${h}px)`,
+ L: `rotateY(-90deg) translateZ(${h}px)`,
+ R: `rotateY(90deg) translateZ(${h}px)`
+ };
+ }
+
+ function createCubie(x, y, z) {
+ const el = document.createElement('div');
+ el.className = 'cubie';
+ el.style.setProperty('--size', SIZE + 'px');
+
+ const colors = {};
+ // Which stickers exist based on position
+ if (y === 1) colors.U = 'U';
+ if (y === -1) colors.D = 'D';
+ if (z === 1) colors.F = 'F';
+ if (z === -1) colors.B = 'B';
+ if (x === -1) colors.L = 'L';
+ if (x === 1) colors.R = 'R';
+
+ const transforms = stickerTransforms(SIZE);
+ FACES.forEach(f => {
+ const st = document.createElement('div');
+ st.className = 'sticker ' + (colors[f] || 'inner');
+ st.dataset.face = f;
+ st.style.transform = transforms[f];
+ el.appendChild(st);
+ });
+
+ const cubie = {
+ el,
+ x, y, z,
+ // Orientation: which world direction each local face points to
+ // We track position only and recolor stickers on reset; for turns we rotate the element
+ qx: x, qy: y, qz: z,
+ // Cumulative rotation as CSS transform string parts
+ rot: ''
+ };
+ updateCubieTransform(cubie, false);
+ cubeRoot.appendChild(el);
+ return cubie;
+ }
+
+ function updateCubieTransform(c, animate) {
+ if (!animate) {
+ c.el.style.transition = 'none';
+ } else {
+ c.el.style.transition = 'transform 0.28s ease-in-out';
+ }
+ const tx = c.qx * STEP;
+ const ty = c.qy * STEP;
+ const tz = c.qz * STEP;
+ c.el.style.transform = `translate3d(${tx}px, ${-ty}px, ${tz}px) ${c.rot}`;
+ if (!animate) {
+ // Force reflow then restore transition
+ void c.el.offsetWidth;
+ c.el.style.transition = 'transform 0.28s ease-in-out';
+ }
+ }
+
+ function initCube() {
+ cubeRoot.innerHTML = '';
+ cubies = [];
+ for (let x = -1; x <= 1; x++) {
+ for (let y = -1; y <= 1; y++) {
+ for (let z = -1; z <= 1; z++) {
+ cubies.push(createCubie(x, y, z));
+ }
+ }
+ }
+ }
+
+ function roundCoord(v) {
+ return Math.round(v);
+ }
+
+ // Multiply quaternion-like rotation: apply axis-angle 90deg to a vector
+ function rotateVec(vx, vy, vz, axis, angleDeg) {
+ const rad = angleDeg * Math.PI / 180;
+ const c = Math.cos(rad), s = Math.sin(rad);
+ const [ax, ay, az] = axis;
+ // Rodrigues
+ const dot = ax * vx + ay * vy + az * vz;
+ const cx = ay * vz - az * vy;
+ const cy = az * vx - ax * vz;
+ const cz = ax * vy - ay * vx;
+ return [
+ vx * c + cx * s + ax * dot * (1 - c),
+ vy * c + cy * s + ay * dot * (1 - c),
+ vz * c + cz * s + az * dot * (1 - c)
+ ];
+ }
+
+ function axisToCSS(axis, angle) {
+ return `rotate3d(${axis[0]}, ${-axis[1]}, ${axis[2]}, ${angle}deg)`;
+ }
+
+ function setButtonsDisabled(dis) {
+ document.querySelectorAll('.controls button').forEach(b => { b.disabled = dis; });
+ }
+
+ function turnFace(face, cw) {
+ if (animating) return Promise.resolve();
+ animating = true;
+ setButtonsDisabled(true);
+
+ const axis = TURN_AXIS[face];
+ const angle = cw ? 90 : -90;
+ const layer = FACE_AXIS[face];
+ // Select cubies on this face: position matching the face normal component = ±1
+ const sel = cubies.filter(c => {
+ if (face === 'U') return c.qy === 1;
+ if (face === 'D') return c.qy === -1;
+ if (face === 'F') return c.qz === 1;
+ if (face === 'B') return c.qz === -1;
+ if (face === 'L') return c.qx === -1;
+ if (face === 'R') return c.qx === 1;
+ });
+
+ // Animate: prepend rotation around cube origin
+ // We need rotation around the cube center, so:
+ // transform = rot * translate * oldRot
+ // Actually cubie transform is translate then rot. For face turn around origin:
+ // new = faceRot * (translate * localRot) = (faceRot * translate * faceRot^-1) * faceRot * localRot
+ // Easier approach: temporarily set transform to faceRot * translate * localRot during animation,
+ // then bake into new position + new local rot.
+
+ sel.forEach(c => {
+ c.el.style.transition = 'transform 0.28s ease-in-out';
+ const tx = c.qx * STEP;
+ const ty = -c.qy * STEP;
+ const tz = c.qz * STEP;
+ const faceRot = axisToCSS(axis, angle);
+ c.el.style.transform = `${faceRot} translate3d(${tx}px, ${ty}px, ${tz}px) ${c.rot}`;
+ });
+
+ return new Promise(resolve => {
+ setTimeout(() => {
+ sel.forEach(c => {
+ // Update logical position
+ const [nx, ny, nz] = rotateVec(c.qx, c.qy, c.qz, axis, angle);
+ c.qx = roundCoord(nx);
+ c.qy = roundCoord(ny);
+ c.qz = roundCoord(nz);
+ // Bake rotation into local rot
+ c.rot = `${axisToCSS(axis, angle)} ${c.rot}`;
+ // Snap without animation
+ c.el.style.transition = 'none';
+ const tx = c.qx * STEP;
+ const ty = -c.qy * STEP;
+ const tz = c.qz * STEP;
+ c.el.style.transform = `translate3d(${tx}px, ${ty}px, ${tz}px) ${c.rot}`;
+ void c.el.offsetWidth;
+ c.el.style.transition = 'transform 0.28s ease-in-out';
+ });
+ animating = false;
+ setButtonsDisabled(false);
+ resolve();
+ }, 300);
+ });
+ }
+
+ async function scramble() {
+ if (animating) return;
+ const faces = FACES.slice();
+ const moves = 25;
+ setButtonsDisabled(true);
+ for (let i = 0; i < moves; i++) {
+ const f = faces[Math.floor(Math.random() * 6)];
+ const cw = Math.random() < 0.5;
+ // Direct call without button lock thrashing
+ animating = false;
+ await turnFace(f, cw);
+ }
+ }
+
+ function reset() {
+ if (animating) return;
+ initCube();
+ }
+
+ // Build buttons
+ const faceBtns = document.getElementById('faceBtns');
+ const faceBtnsCCW = document.getElementById('faceBtnsCCW');
+ FACES.forEach(f => {
+ const b = document.createElement('button');
+ b.textContent = f;
+ b.title = f + ' clockwise';
+ b.addEventListener('click', () => turnFace(f, true));
+ faceBtns.appendChild(b);
+
+ const b2 = document.createElement('button');
+ b2.textContent = f + "'";
+ b2.title = f + ' counter-clockwise';
+ b2.addEventListener('click', () => turnFace(f, false));
+ faceBtnsCCW.appendChild(b2);
+ });
+
+ document.getElementById('scrambleBtn').addEventListener('click', scramble);
+ document.getElementById('resetBtn').addEventListener('click', reset);
+
+ initCube();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e85044630905/grok.png b/data/artifacts/e85044630905/grok.png
new file mode 100644
index 0000000..4153a67
Binary files /dev/null and b/data/artifacts/e85044630905/grok.png differ
diff --git a/data/artifacts/e85044630905/hermes3-8b.html b/data/artifacts/e85044630905/hermes3-8b.html
new file mode 100644
index 0000000..450c5ae
--- /dev/null
+++ b/data/artifacts/e85044630905/hermes3-8b.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>3D Rubik's Cube</title>
+ <style>
+ body {
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
+ touch-action: none;
+ cursor: grab;
+ }
+
+ #cube {
+ width: 100px;
+ height: 100px;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%) rotateY(0deg);
+ perspective: 800px;
+ }
+
+ .face {
+ width: 100px;
+ height: 100px;
+ position: absolute;
+ background-color: #ccc;
+ backface-visibility: hidden;
+ }
+
+ .front {
+ transform-origin: center;
+ background-color: #f00;
+ }
+
+ .right {
+ transform-origin: left;
+ background-color: #0f0;
+ transform: rotateY(90deg);
+ }
+
+ .left {
+ transform-origin: right;
+ background-color: #00f;
+ transform: rotateY(-90deg);
+ }
+
+ .top {
+ transform-origin: bottom;
+ background-color: #ff0;
+ transform: rotateX(90deg);
+ }
+
+ .bottom {
+ transform-origin: top;
+ background-color: #f95;
+ transform: rotateX(-90deg);
+ }
+
+ .back {
+ transform-origin: center;
+ background-color: #950;
+ transform: rotateY(180deg);
+ }
+
+ /* Scramble pattern */
+ .scrambled-front {
+ background-color: #faa;
+ }
+ .scrambled-right { background-color: #ad5; }
+ .scrambled-left { background-color: #58f; }
+ .scrambled-top { background-color: #0f9; }
+ .scrambled-bottom { background-color: #95a; }
+ .scrambled-back { background-color: #8d3; }
+
+ /* Buttons */
+ button {
+ position: absolute;
+ top: 10px;
+ left: calc(50% - 100px/2);
+ transform-origin: center;
+ width: 60px;
+ height: 40px;
+ border: none;
+ background-color: #fff;
+ color: #000;
+ font-size: 20px;
+ outline: none;
+ cursor: pointer;
+ }
+
+ #rotate-front {
+ top: 10px;
+ left: calc(50% - 170px/2);
+ transform: rotate(-45deg);
+ }
+
+ #rotate-right {
+ top: 10px;
+ left: calc(50% + 40px);
+ transform: rotateY(90deg) translateZ(100px) rotate(45deg);
+ }
+
+ #reset {
+ bottom: 10px;
+ left: calc(50% - 140px/2);
+ }
+
+ #scramble {
+ bottom: 10px;
+ right: 10px;
+ }
+
+ </style>
+</head>
+<body>
+
+<div id="cube">
+ <div class="face front"></div>
+ <div class="face right"></div>
+ <div class="face left"></div>
+ <div class="face top"></div>
+ <div class="face bottom"></div>
+ <div class="face back"></div>
+</div>
+
+<button id="rotate-front">Front</button>
+<button id="rotate-right">Right</button>
+<button id="reset">Reset</button>
+<button id="scramble">Scramble</button>
+
+<script>
+ let rotateX = 0;
+ let rotateY = 0;
+
+ document.addEventListener("mousedown", function(e) {
+ if (e.target === document.body) {
+ document.querySelector("#cube").addEventListener("mousemove", function(e) {
+ const rect = this.getBoundingClientRect();
+ const relX = e.clientX - rect.left;
+ const relY = e.clientY - rect.top;
+
+ this.style.transform = "translate(-50%, -50%) rotateY(" + rotateY + "deg) rotateX(" + rotateX + "deg)";
+ });
+
+ document.querySelector("#cube").addEventListener("mouseup", function() {
+ document.querySelector("#cube").removeEventListener("mousemove", null);
+ });
+ }
+ });
+
+ const buttons = document.querySelectorAll("button");
+
+ for (const btn of buttons) {
+ btn.addEventListener("click", function() {
+ if (this.id.startsWith("rotate")) {
+ rotateY = this.id.endsWith("right") ? 90 : -90;
+ setTimeout(function() {
+ this.style.transformOrigin = "center";
+ this.style.background = this.classList[1] + " scrambled-" + this.classList[2];
+ }, 200, this);
+ } else if (this.id === "reset") {
+ document.querySelector("#cube").style.transform = "translate(-50%, -50%) rotateY(0deg) rotateX(0deg)";
+ } else if (this.id === "scramble") {
+ document.querySelectorAll(".face").forEach(function(face) {
+ const cls = face.className.split(" ");
+ face.style.transformOrigin = "center";
+ face.style.background = cls[2] + " scrambled-" + cls[4];
+ });
+ }
+ });
+ }
+
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e85044630905/hermes3-8b.png b/data/artifacts/e85044630905/hermes3-8b.png
new file mode 100644
index 0000000..212de50
Binary files /dev/null and b/data/artifacts/e85044630905/hermes3-8b.png differ
diff --git a/data/artifacts/e85044630905/hf-qwen-coder-32b.html b/data/artifacts/e85044630905/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..08d2965
--- /dev/null
+++ b/data/artifacts/e85044630905/hf-qwen-coder-32b.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>3D Rubik's Cube</title>
+<style>
+body { margin: 0; overflow: hidden; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #222; }
+#cube-container { perspective: 800px; position: relative; width: 300px; height: 300px; touch-action: none; }
+.cube { transform-style: preserve-3d; width: 100%; height: 100%; animation: rotateCube 20s infinite linear; }
+.face { position: absolute; width: 98%; height: 98%; background-color: #ccc; border: 1px solid #444; box-sizing: border-box; }
+.front { transform: translateZ(150px); background-color: #f9a602; }
+.back { transform: rotateY(180deg) translateZ(150px); background-color: #ff3e2e; }
+.right { transform: rotateY(90deg) translateZ(150px); background-color: #6d4cfc; }
+.left { transform: rotateY(-90deg) translateZ(150px); background-color: #8dd331; }
+.top { transform: rotateX(90deg) translateZ(150px); background-color: #fff; }
+.bottom { transform: rotateX(-90deg) translateZ(150px); background-color: #f2ff4c; }
+button { position: absolute; bottom: 20px; background-color: #333; color: white; border: none; padding: 10px; cursor: pointer; z-index: 10; }
+#scramble { left: 50%; transform: translateX(-50%); }
+#reset { right: 20px; }
+@keyframes rotateCube {
+ from { transform: rotateY(0deg) rotateX(0deg); }
+ to { transform: rotateY(360deg) rotateX(360deg); }
+}
+</style>
+</head>
+<body>
+<div id="cube-container">
+ <div class="cube" id="cube">
+ <div class="face front"></div>
+ <div class="face back"></div>
+ <div class="face right"></div>
+ <div class="face left"></div>
+ <div class="face top"></div>
+ <div class="face bottom"></div>
+ </div>
+ <button id="scramble">Scramble</button>
+ <button id="reset">Reset</button>
+</div>
+<script>
+const cube = document.getElementById('cube');
+let isDragging = false;
+let startX, startY;
+let currentX = 0, currentY = -35;
+
+document.getElementById('cube-container').addEventListener('mousedown', startDrag);
+document.addEventListener('mouseup', stopDrag);
+document.addEventListener('mousemove', drag);
+
+function startDrag(e) {
+ e.preventDefault();
+ isDragging = true;
+ startX = e.clientX;
+ startY = e.clientY;
+}
+
+function stopDrag() {
+ isDragging = false;
+}
+
+function drag(e) {
+ if (!isDragging) return;
+ const deltaX = e.clientX - startX;
+ const deltaY = e.clientY - startY;
+ currentX += deltaX * 0.3;
+ currentY += deltaY * 0.3;
+ cube.style.transform = `rotateX(${currentY}deg) rotateY(${currentX}deg)`;
+ startX = e.clientX;
+ startY = e.clientY;
+}
+
+const faces = ['front', 'back', 'right', 'left', 'top', 'bottom'];
+const moves = [
+ { face: 'front', axis: 'y', direction: 1 },
+ { face: 'back', axis: 'y', direction: -1 },
+ { face: 'right', axis: 'x', direction: -1 },
+ { face: 'left', axis: 'x', direction: 1 },
+ { face: 'top', axis: 'z', direction: -1 },
+ { face: 'bottom', axis: 'z', direction: 1 }
+];
+
+function rotateFace(face, axis, direction) {
+ const faceElement = document.querySelector(`.face.${face}`);
+ let startAngle = 0;
+ const animation = setInterval(() => {
+ startAngle += direction * 9;
+ faceElement.style.transform = `rotate${axis}(${startAngle}deg) translateZ(150px)`;
+ if (startAngle === direction * 90) clearInterval(animation);
+ }, 20);
+}
+
+document.getElementById('scramble').addEventListener('click', () => {
+ for (let i = 0; i < 30; i++) {
+ const move = moves[Math.floor(Math.random() * moves.length)];
+ setTimeout(() => rotateFace(move.face, move.axis, move.direction), i * 100);
+ }
+});
+
+document.getElementById('reset').addEventListener('click', () => {
+ cube.style.transform = 'rotateX(-35deg) rotateY(0deg)';
+});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e85044630905/hf-qwen-coder-32b.png b/data/artifacts/e85044630905/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..beb875a
Binary files /dev/null and b/data/artifacts/e85044630905/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/e85044630905/kimi.html b/data/artifacts/e85044630905/kimi.html
new file mode 100644
index 0000000..0c41a79
--- /dev/null
+++ b/data/artifacts/e85044630905/kimi.html
@@ -0,0 +1,367 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>3D Rubik's Cube</title>
+<style>
+body {
+ margin: 0;
+ overflow: hidden;
+ background: #1a1a1a;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ touch-action: none;
+}
+
+.scene {
+ width: 100vw;
+ height: 100vh;
+ perspective: 1200px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: grab;
+}
+
+.scene:active {
+ cursor: grabbing;
+}
+
+.cube {
+ position: relative;
+ width: 0;
+ height: 0;
+ transform-style: preserve-3d;
+ transform: rotateX(-30deg) rotateY(-45deg);
+}
+
+.cubie {
+ position: absolute;
+ width: 2em;
+ height: 2em;
+ transform-style: preserve-3d;
+ transition: transform 0.4s cubic-bezier(0.4, 0.0, 0.2, 1);
+}
+
+.face {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ border: 2px solid #000;
+ border-radius: 4px;
+ box-sizing: border-box;
+ backface-visibility: hidden;
+ box-shadow: inset 0 0 10px rgba(0,0,0,0.3);
+}
+
+.face-xp { transform: rotateY(90deg) translateZ(1em); }
+.face-xn { transform: rotateY(-90deg) translateZ(1em); }
+.face-yp { transform: rotateX(90deg) translateZ(1em); }
+.face-yn { transform: rotateX(-90deg) translateZ(1em); }
+.face-zp { transform: translateZ(1em); }
+.face-zn { transform: rotateY(180deg) translateZ(1em); }
+
+.color-xp { background: #dc2626; } /* Red - Right */
+.color-xn { background: #ea580c; } /* Orange - Left */
+.color-yp { background: #f3f4f6; } /* White - Up */
+.color-yn { background: #facc15; } /* Yellow - Down */
+.color-zp { background: #16a34a; } /* Green - Front */
+.color-zn { background: #2563eb; } /* Blue - Back */
+.color-internal { background: #111827; }
+
+.controls {
+ position: absolute;
+ bottom: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
+ gap: 8px;
+ background: rgba(0,0,0,0.8);
+ padding: 20px;
+ border-radius: 12px;
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255,255,255,0.1);
+}
+
+button {
+ padding: 12px 16px;
+ cursor: pointer;
+ font-weight: bold;
+ border: none;
+ border-radius: 6px;
+ background: #374151;
+ color: white;
+ font-size: 14px;
+ transition: all 0.2s;
+ text-transform: uppercase;
+}
+
+button:hover {
+ background: #4b5563;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
+}
+
+button:active {
+ transform: translateY(0);
+}
+
+.scramble {
+ grid-column: span 3;
+ background: #7c3aed;
+}
+
+.scramble:hover { background: #8b5cf6; }
+
+.reset {
+ grid-column: span 3;
+ background: #dc2626;
+}
+
+.reset:hover { background: #ef4444; }
+
+.info {
+ position: absolute;
+ top: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ color: rgba(255,255,255,0.6);
+ text-align: center;
+ pointer-events: none;
+ font-size: 14px;
+}
+</style>
+</head>
+<body>
+
+<div class="scene" id="scene">
+ <div class="cube" id="cube"></div>
+</div>
+
+<div class="info">Drag to rotate view • Click buttons to turn faces</div>
+
+<div class="controls">
+ <button onclick="doMove('U')">U</button>
+ <button onclick="doMove('D')">D</button>
+ <button onclick="doMove('F')">F</button>
+ <button onclick="doMove('B')">B</button>
+ <button onclick="doMove('L')">L</button>
+ <button onclick="doMove('R')">R</button>
+ <button class="scramble" onclick="scramble()">Scramble</button>
+ <button class="reset" onclick="resetCube()">Reset</button>
+</div>
+
+<script>
+const cube = document.getElementById('cube');
+const scene = document.getElementById('scene');
+let cubies = [];
+let isDragging = false;
+let lastX = 0, lastY = 0;
+let rotX = -30, rotY = -45;
+
+// Initialize cube
+function init() {
+ cubies = [];
+ cube.innerHTML = '';
+
+ // Create 26 cubies (3x3x3 minus center)
+ for (let x = -1; x <= 1; x++) {
+ for (let y = -1; y <= 1; y++) {
+ for (let z = -1; z <= 1; z++) {
+ if (x === 0 && y === 0 && z === 0) continue;
+ createCubie(x, y, z);
+ }
+ }
+ }
+ render();
+}
+
+function createCubie(x, y, z) {
+ const el = document.createElement('div');
+ el.className = 'cubie';
+
+ // Determine which faces are external and their colors
+ const faces = [
+ { cls: 'face-xp', color: x === 1 ? 'color-xp' : 'color-internal' },
+ { cls: 'face-xn', color: x === -1 ? 'color-xn' : 'color-internal' },
+ { cls: 'face-yp', color: y === 1 ? 'color-yp' : 'color-internal' },
+ { cls: 'face-yn', color: y === -1 ? 'color-yn' : 'color-internal' },
+ { cls: 'face-zp', color: z === 1 ? 'color-zp' : 'color-internal' },
+ { cls: 'face-zn', color: z === -1 ? 'color-zn' : 'color-internal' }
+ ];
+
+ faces.forEach(f => {
+ const face = document.createElement('div');
+ face.className = `face ${f.cls} ${f.color}`;
+ el.appendChild(face);
+ });
+
+ cube.appendChild(el);
+
+ const cubie = { x, y, z, rx: 0, ry: 0, rz: 0, el };
+ cubies.push(cubie);
+ return cubie;
+}
+
+function render() {
+ cubies.forEach(c => {
+ updateTransform(c);
+ });
+}
+
+function updateTransform(c) {
+ c.el.style.transform = `translate3d(${c.x * 2}em, ${c.y * 2}em, ${c.z * 2}em) rotateX(${c.rx}deg) rotateY(${c.ry}deg) rotateZ(${c.rz}deg)`;
+}
+
+// Rotation logic
+const moves = {
+ 'U': { axis: 'y', layer: 1, angle: 90 },
+ 'D': { axis: 'y', layer: -1, angle: -90 },
+ 'F': { axis: 'z', layer: 1, angle: -90 },
+ 'B': { axis: 'z', layer: -1, angle: 90 },
+ 'L': { axis: 'x', layer: -1, angle: -90 },
+ 'R': { axis: 'x', layer: 1, angle: 90 }
+};
+
+function rotatePoint(p, axis, angle) {
+ const rad = angle * Math.PI / 180;
+ const cos = Math.round(Math.cos(rad));
+ const sin = Math.round(Math.sin(rad));
+ let x = p.x, y = p.y, z = p.z;
+
+ if (axis === 'x') {
+ return {
+ x: x,
+ y: y * cos - z * sin,
+ z: y * sin + z * cos
+ };
+ } else if (axis === 'y') {
+ return {
+ x: x * cos + z * sin,
+ y: y,
+ z: -x * sin + z * cos
+ };
+ } else if (axis === 'z') {
+ return {
+ x: x * cos - y * sin,
+ y: x * sin + y * cos,
+ z: z
+ };
+ }
+ return p;
+}
+
+function doMove(move, instant = false) {
+ const m = moves[move];
+ if (!m) return;
+
+ const affected = cubies.filter(c => Math.round(c[m.axis]) === m.layer);
+
+ if (instant) {
+ affected.forEach(c => {
+ c.el.style.transition = 'none';
+ });
+ }
+
+ affected.forEach(c => {
+ // Update position
+ const newPos = rotatePoint({ x: c.x, y: c.y, z: c.z }, m.axis, m.angle);
+ c.x = newPos.x;
+ c.y = newPos.y;
+ c.z = newPos.z;
+
+ // Update orientation
+ if (m.axis === 'x') c.rx += m.angle;
+ if (m.axis === 'y') c.ry += m.angle;
+ if (m.axis === 'z') c.rz += m.angle;
+
+ updateTransform(c);
+ });
+
+ if (instant) {
+ // Force reflow
+ void cube.offsetWidth;
+ setTimeout(() => {
+ affected.forEach(c => {
+ c.el.style.transition = '';
+ });
+ }, 50);
+ }
+}
+
+// Mouse controls
+scene.addEventListener('mousedown', (e) => {
+ isDragging = true;
+ lastX = e.clientX;
+ lastY = e.clientY;
+});
+
+document.addEventListener('mousemove', (e) => {
+ if (!isDragging) return;
+ const deltaX = e.clientX - lastX;
+ const deltaY = e.clientY - lastY;
+ rotY += deltaX * 0.5;
+ rotX -= deltaY * 0.5;
+ cube.style.transform = `rotateX(${rotX}deg) rotateY(${rotY}deg)`;
+ lastX = e.clientX;
+ lastY = e.clientY;
+});
+
+document.addEventListener('mouseup', () => {
+ isDragging = false;
+});
+
+// Touch support
+scene.addEventListener('touchstart', (e) => {
+ isDragging = true;
+ lastX = e.touches[0].clientX;
+ lastY = e.touches[0].clientY;
+ e.preventDefault();
+}, { passive: false });
+
+document.addEventListener('touchmove', (e) => {
+ if (!isDragging) return;
+ const deltaX = e.touches[0].clientX - lastX;
+ const deltaY = e.touches[0].clientY - lastY;
+ rotY += deltaX * 0.5;
+ rotX -= deltaY * 0.5;
+ cube.style.transform = `rotateX(${rotX}deg) rotateY(${rotY}deg)`;
+ lastX = e.touches[0].clientX;
+ lastY = e.touches[0].clientY;
+ e.preventDefault();
+}, { passive: false });
+
+document.addEventListener('touchend', () => {
+ isDragging = false;
+});
+
+// Scramble
+function scramble() {
+ const keys = Object.keys(moves);
+ // Disable transitions for instant scramble
+ cubies.forEach(c => c.el.style.transition = 'none');
+
+ for (let i = 0; i < 20; i++) {
+ const move = keys[Math.floor(Math.random() * keys.length)];
+ doMove(move, true);
+ }
+
+ // Re-enable transitions
+ requestAnimationFrame(() => {
+ cubies.forEach(c => {
+ c.el.style.transition = '';
+ });
+ });
+}
+
+// Reset
+function resetCube() {
+ init();
+}
+
+// Start
+init();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e85044630905/kimi.png b/data/artifacts/e85044630905/kimi.png
new file mode 100644
index 0000000..256af60
Binary files /dev/null and b/data/artifacts/e85044630905/kimi.png differ
diff --git a/data/artifacts/e85044630905/qwen25-7b.html b/data/artifacts/e85044630905/qwen25-7b.html
new file mode 100644
index 0000000..4d53e97
--- /dev/null
+++ b/data/artifacts/e85044630905/qwen25-7b.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>3D Rubik's Cube</title>
+ <style>
+ body { margin: 0; }
+ canvas {
+ display: block;
+ width: 100vw;
+ height: 100vh;
+ background-color: #eee;
+ touch-action: none;
+ user-select: none;
+ }
+ button { position: absolute; top: 20px; left: 20px; z-index: 10; }
+ </style>
+</head>
+<body>
+ <canvas id="cube"></canvas>
+ <button onclick="rotate('L', -90)">Left Face</button>
+ <button onclick="rotate('R', 90)">Right Face</button>
+ <button onclick="rotate('U', -90)">Up Face</button>
+ <button onclick="rotate('D', 90)">Down Face</button>
+ <button onclick="scramble()">Scramble</button>
+ <script>
+ const canvas = document.getElementById('cube');
+ const ctx = canvas.getContext('2d');
+
+ let rotationX = 0;
+ let rotationY = 0;
+
+ function drawCube() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for (let x = -1; x <= 1; x++) {
+ for (let y = -1; y <= 1; y++) {
+ for (let z = -1; z <= 1; z++) {
+ let dx = x * 20;
+ let dy = y * 20;
+ let dz = z * 20;
+
+ if (x === 0 && y === 0) continue;
+
+ ctx.save();
+ ctx.translate(canvas.width / 2, canvas.height / 2);
+ ctx.rotateX(rotationX);
+ ctx.rotateY(rotationY);
+ ctx.scale(1, -1);
+ ctx.translate(-dx, -dy);
+
+ ctx.beginPath();
+ ctx.arc(0, 0, 5, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.restore();
+ }
+ }
+ }
+ }
+
+ function rotate(face, degrees) {
+ rotationX += face === 'U' || face === 'D' ? degrees : 0;
+ rotationY += face === 'L' || face === 'R' ? degrees : 0;
+ drawCube();
+ }
+
+ function scramble() {
+ for (let i = 0; i < 10; i++) {
+ let direction = Math.random() > 0.5 ? -90 : 90;
+ rotate('U', direction);
+ rotate('D', direction);
+ rotate('L', direction);
+ rotate('R', direction);
+ }
+ }
+
+ function init() {
+ canvas.addEventListener('mousedown', (e) => {
+ document.addEventListener('mousemove', moveCamera, false);
+ document.addEventListener('mouseup', () => { document.removeEventListener('mousemove', moveCamera); }, false);
+ });
+ document.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ document.addEventListener('touchmove', moveCamera, false);
+ document.addEventListener('touchend', () => { document.removeEventListener('touchmove', moveCamera); }, false);
+ });
+
+ function moveCamera(e) {
+ if ('touches' in e) {
+ let touch = e.touches[0];
+ rotationX += (touch.clientY - canvas.height / 2) * 0.1;
+ rotationY += (canvas.width / 2 - touch.clientX) * 0.1;
+ } else {
+ let mouse = e;
+ rotationX += (mouse.clientY - canvas.height / 2) * 0.1;
+ rotationY += (canvas.width / 2 - mouse.clientX) * 0.1;
+ }
+ drawCube();
+ }
+
+ window.addEventListener('resize', () => { canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; });
+ }
+
+ canvas.width = innerWidth;
+ canvas.height = innerHeight;
+
+ init();
+
+ drawCube();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e85044630905/qwen25-7b.png b/data/artifacts/e85044630905/qwen25-7b.png
new file mode 100644
index 0000000..ecc8c97
Binary files /dev/null and b/data/artifacts/e85044630905/qwen25-7b.png differ
diff --git a/data/artifacts/e85044630905/qwen3-14b.html b/data/artifacts/e85044630905/qwen3-14b.html
new file mode 100644
index 0000000..7933ee9
--- /dev/null
+++ b/data/artifacts/e85044630905/qwen3-14b.html
@@ -0,0 +1,374 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>3D Rubik's Cube</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ width: 100%;
+ height: 100%;
+ background: #111;
+ font-family: sans-serif;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ }
+ canvas {
+ width: 100%;
+ height: 100%;
+ display: block;
+ }
+ .controls {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 10px;
+ }
+ .controls button {
+ padding: 8px 12px;
+ background: #333;
+ border: 1px solid #555;
+ color: white;
+ cursor: pointer;
+ border-radius: 4px;
+ font-size: 14px;
+ }
+ .controls button:hover {
+ background: #444;
+ }
+ </style>
+</head>
+<body>
+ <div class="controls">
+ <button onclick="scramble()">Scramble</button>
+ <button onclick="resetCube()">Reset</button>
+ <button onclick="rotateFace('U', 1)">U</button>
+ <button onclick="rotateFace('U', -1)">U'</button>
+ <button onclick="rotateFace('D', 1)">D</button>
+ <button onclick="rotateFace('D', -1)">D'</button>
+ <button onclick="rotateFace('L', 1)">L</button>
+ <button onclick="rotateFace('L', -1)">L'</button>
+ <button onclick="rotateFace('R', 1)">R</button>
+ <button onclick="rotateFace('R', -1)">R'</button>
+ <button onclick="rotateFace('F', 1)">F</button>
+ <button onclick="rotateFace('F', -1)">F'</button>
+ <button onclick="rotateFace('B', 1)">B</button>
+ <button onclick="rotateFace('B', -1)">B'</button>
+ </div>
+ <canvas id="cubeCanvas"></canvas>
+ <script>
+ const canvas = document.getElementById("cubeCanvas");
+ const ctx = canvas.getContext("2d");
+ const width = canvas.width = window.innerWidth;
+ const height = canvas.height = window.innerHeight;
+ let isDragging = false;
+ let lastX, lastY;
+ let angleX = 0, angleY = 0;
+ let cube = [];
+ let animationId = null;
+
+ function initCube() {
+ const faces = ['U', 'D', 'L', 'R', 'F', 'B'];
+ for (let i = 0; i < 6; i++) {
+ cube[i] = [];
+ for (let j = 0; j < 9; j++) {
+ cube[i][j] = faces[i].charCodeAt(0);
+ }
+ }
+ }
+
+ function resetCube() {
+ cancelAnimationFrame(animationId);
+ initCube();
+ draw();
+ }
+
+ function scramble() {
+ cancelAnimationFrame(animationId);
+ const moves = ["U", "U'", "D", "D'", "L", "L'", "R", "R'", "F", "F'", "B", "B'"];
+ for (let i = 0; i < 20; i++) {
+ const move = moves[Math.floor(Math.random() * moves.length)];
+ rotateFace(move[0], move[1] === "'" ? -1 : 1);
+ }
+ }
+
+ function rotateFace(face, dir) {
+ cancelAnimationFrame(animationId);
+ const faceIdx = face.charCodeAt(0) - 'U'.charCodeAt(0);
+ const faceCenter = Math.floor(9 / 2);
+ const temp = [...cube[faceIdx]];
+
+ if (dir === 1) {
+ // Rotate face clockwise
+ cube[faceIdx][0] = temp[6];
+ cube[faceIdx][1] = temp[3];
+ cube[faceIdx][2] = temp[0];
+ cube[faceIdx][3] = temp[7];
+ cube[faceIdx][5] = temp[1];
+ cube[faceIdx][6] = temp[8];
+ cube[faceIdx][7] = temp[5];
+ cube[faceIdx][8] = temp[2];
+ } else {
+ // Rotate face counter-clockwise
+ cube[faceIdx][0] = temp[2];
+ cube[faceIdx][1] = temp[5];
+ cube[faceIdx][2] = temp[8];
+ cube[faceIdx][3] = temp[0];
+ cube[faceIdx][5] = temp[7];
+ cube[faceIdx][6] = temp[3];
+ cube[faceIdx][7] = temp[6];
+ cube[faceIdx][8] = temp[1];
+ }
+
+ // Rotate adjacent faces
+ if (face === 'U') {
+ const temp = [cube[3][0], cube[3][1], cube[3][2]];
+ cube[3][0] = cube[5][0];
+ cube[3][1] = cube[5][1];
+ cube[3][2] = cube[5][2];
+ cube[5][0] = cube[2][0];
+ cube[5][1] = cube[2][1];
+ cube[5][2] = cube[2][2];
+ cube[2][0] = cube[4][0];
+ cube[2][1] = cube[4][1];
+ cube[2][2] = cube[4][2];
+ cube[4][0] = temp[0];
+ cube[4][1] = temp[1];
+ cube[4][2] = temp[2];
+ } else if (face === 'D') {
+ const temp = [cube[2][6], cube[2][7], cube[2][8]];
+ cube[2][6] = cube[4][6];
+ cube[2][7] = cube[4][7];
+ cube[2][8] = cube[4][8];
+ cube[4][6] = cube[5][6];
+ cube[4][7] = cube[5][7];
+ cube[4][8] = cube[5][8];
+ cube[5][6] = cube[3][6];
+ cube[5][7] = cube[3][7];
+ cube[5][8] = cube[3][8];
+ cube[3][6] = temp[0];
+ cube[3][7] = temp[1];
+ cube[3][8] = temp[2];
+ } else if (face === 'L') {
+ const temp = [cube[0][0], cube[0][3], cube[0][6]];
+ cube[0][0] = cube[4][0];
+ cube[0][3] = cube[4][3];
+ cube[0][6] = cube[4][6];
+ cube[4][0] = cube[1][0];
+ cube[4][3] = cube[1][3];
+ cube[4][6] = cube[1][6];
+ cube[1][0] = cube[5][8];
+ cube[1][3] = cube[5][5];
+ cube[1][6] = cube[5][2];
+ cube[5][2] = temp[0];
+ cube[5][5] = temp[1];
+ cube[5][8] = temp[2];
+ } else if (face === 'R') {
+ const temp = [cube[0][2], cube[0][5], cube[0][8]];
+ cube[0][2] = cube[5][6];
+ cube[0][5] = cube[5][3];
+ cube[0][8] = cube[5][0];
+ cube[5][0] = cube[1][2];
+ cube[5][3] = cube[1][5];
+ cube[5][6] = cube[1][8];
+ cube[1][2] = cube[4][8];
+ cube[1][5] = cube[4][5];
+ cube[1][8] = cube[4][2];
+ cube[4][2] = temp[0];
+ cube[4][5] = temp[1];
+ cube[4][8] = temp[2];
+ } else if (face === 'F') {
+ const temp = [cube[0][6], cube[0][7], cube[0][8]];
+ cube[0][6] = cube[2][6];
+ cube[0][7] = cube[2][7];
+ cube[0][8] = cube[2][8];
+ cube[2][6] = cube[1][8];
+ cube[2][7] = cube[1][5];
+ cube[2][8] = cube[1][2];
+ cube[1][2] = cube[5][0];
+ cube[1][5] = cube[5][3];
+ cube[1][8] = cube[5][6];
+ cube[5][0] = temp[0];
+ cube[5][3] = temp[1];
+ cube[5][6] = temp[2];
+ } else if (face === 'B') {
+ const temp = [cube[0][0], cube[0][1], cube[0][2]];
+ cube[0][0] = cube[5][8];
+ cube[0][1] = cube[5][5];
+ cube[0][2] = cube[5][2];
+ cube[5][2] = cube[1][0];
+ cube[5][5] = cube[1][3];
+ cube[5][8] = cube[1][6];
+ cube[1][0] = cube[2][2];
+ cube[1][3] = cube[2][1];
+ cube[1][6] = cube[2][0];
+ cube[2][0] = temp[0];
+ cube[2][1] = temp[1];
+ cube[2][2] = temp[2];
+ }
+
+ draw();
+ }
+
+ function draw() {
+ ctx.clearRect(0, 0, width, height);
+ const size = 100;
+ const offset = 300;
+ const aspect = width / height;
+ const radius = 500;
+
+ const projMat = [
+ [aspect, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1]
+ ];
+
+ const viewMat = [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, -1 / radius, 1]
+ ];
+
+ const rotX = [
+ [1, 0, 0, 0],
+ [0, Math.cos(angleX), -Math.sin(angleX), 0],
+ [0, Math.sin(angleX), Math.cos(angleX), 0],
+ [0, 0, 0, 1]
+ ];
+
+ const rotY = [
+ [Math.cos(angleY), 0, Math.sin(angleY), 0],
+ [0, 1, 0, 0],
+ [-Math.sin(angleY), 0, Math.cos(angleY), 0],
+ [0, 0, 0, 1]
+ ];
+
+ const modelMat = matMul(matMul(rotX, rotY), [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1]
+ ]);
+
+ const worldMat = matMul(projMat, matMul(viewMat, modelMat));
+
+ const cubeSize = 100;
+ const center = [0, 0, 0];
+ const positions = [];
+ const faceColors = {
+ 'U': [0.8, 0.8, 1.0],
+ 'D': [1.0, 0.8, 0.8],
+ 'L': [0.8, 1.0, 0.8],
+ 'R': [1.0, 0.8, 1.0],
+ 'F': [1.0, 1.0, 0.8],
+ 'B': [0.8, 0.8, 1.0]
+ };
+
+ for (let face = 0; face < 6; face++) {
+ for (let i = 0; i < 3; i++) {
+ for (let j = 0; j < 3; j++) {
+ const x = (i - 1) * cubeSize;
+ const y = (j - 1) * cubeSize;
+ const z = (face === 0 ? 1 : face === 1 ? -1 : face === 2 ? -1 : face === 3 ? 1 : face === 4 ? 1 : -1) * cubeSize;
+ positions.push([x, y, z]);
+ }
+ }
+ }
+
+ ctx.save();
+ ctx.translate(width / 2, height / 2);
+
+ const faces = 6;
+ for (let face = 0; face < faces; face++) {
+ const faceColors = faceColors[String.fromCharCode('U'.charCodeAt(0) + face)];
+ const faceIndices = [];
+ for (let i = 0; i < 9; i++) {
+ faceIndices.push(i);
+ }
+ for (let i = 0; i < 3; i++) {
+ for (let j = 0; j < 3; j++) {
+ const idx = i * 3 + j;
+ const p = positions[face * 9 + idx];
+ const [x, y, z] = matVecMul(worldMat, [p[0], p[1], p[2], 1]);
+ ctx.fillStyle = `rgb(${Math.floor(faceColors[0] * 255)}, ${Math.floor(faceColors[1] * 255)}, ${Math.floor(faceColors[2] * 255)})`;
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + (positions[face * 9 + idx + 1][0] - p[0]), y + (positions[face * 9 + idx + 1][1] - p[1]));
+ ctx.lineTo(x + (positions[face * 9 + idx + 3][0] - p[0]), y + (positions[face * 9 + idx + 3][1] - p[1]));
+ ctx.lineTo(x + (positions[face * 9 + idx + 2][0] - p[0]), y + (positions[face * 9 + idx + 2][1] - p[1]));
+ ctx.closePath();
+ ctx.fill();
+ }
+ }
+ }
+
+ ctx.restore();
+ }
+
+ function matMul(a, b) {
+ const result = [];
+ for (let i = 0; i < 4; i++) {
+ result[i] = [];
+ for (let j = 0; j < 4; j++) {
+ result[i][j] = 0;
+ for (let k = 0; k < 4; k++) {
+ result[i][j] += a[i][k] * b[k][j];
+ }
+ }
+ }
+ return result;
+ }
+
+ function matVecMul(m, v) {
+ const result = [0, 0, 0, 0];
+ for (let i = 0; i < 4; i++) {
+ result[i] = m[i][0] * v[0] + m[i][1] * v[1] + m[i][2] * v[2] + m[i][3] * v[3];
+ }
+ return result;
+ }
+
+ function animate() {
+ draw();
+ animationId = requestAnimationFrame(animate);
+ }
+
+ canvas.addEventListener('mousedown', (e) => {
+ isDragging = true;
+ lastX = e.clientX;
+ lastY = e.clientY;
+ });
+
+ canvas.addEventListener('mousemove', (e) => {
+ if (isDragging) {
+ const dx = e.clientX - lastX;
+ const dy = e.clientY - lastY;
+ angleY += dx * 0.005;
+ angleX += dy * 0.005;
+ lastX = e.clientX;
+ lastY = e.clientY;
+ }
+ });
+
+ canvas.addEventListener('mouseup', () => {
+ isDragging = false;
+ });
+
+ canvas.addEventListener('mouseleave', () => {
+ isDragging = false;
+ });
+
+ initCube();
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e85044630905/qwen3-14b.png b/data/artifacts/e85044630905/qwen3-14b.png
new file mode 100644
index 0000000..da117af
Binary files /dev/null and b/data/artifacts/e85044630905/qwen3-14b.png differ
diff --git a/data/artifacts/f83743a0fa7e/gemma3-12b.html b/data/artifacts/f83743a0fa7e/gemma3-12b.html
new file mode 100644
index 0000000..be039c5
--- /dev/null
+++ b/data/artifacts/f83743a0fa7e/gemma3-12b.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Rentv.com Upgrade Suggestions</title>
+ <style>
+ body { font-family: sans-serif; margin: 20px; }
+ h1 { color: #333; }
+ .suggestion { border: 1px solid #ccc; padding: 15px; margin-bottom: 10px; border-radius: 5px; }
+ .suggestion h2 { font-size: 1.2em; margin-top: 0; color: #555; }
+ .suggestion p { line-height: 1.4; }
+ button { background-color: #4CAF50; border: none; color: white; padding: 10px 20px; text-align: center; text-decoration: none; display: inline-block; font-size: 1em; margin: 5px; cursor: pointer; border-radius: 3px; }
+ </style>
+</head>
+<body>
+
+ <h1>Rentv.com Upgrade Suggestions</h1>
+
+ <div class="suggestion">
+ <h2>Improve Search Filtering</h2>
+ <p>Currently, filtering options are limited. Adding filters for "Pet-Friendly," "Washer/Dryer Included," and "Furnished" would significantly improve user experience.</p>
+ <button onclick="alert('Acknowledged: Adding Pet-Friendly, Washer/Dryer, & Furnished filters.')">Mark as Planned</button>
+ </div>
+
+ <div class="suggestion">
+ <h2>Enhanced Map Integration</h2>
+ <p>The map view is basic. Integrating with a more robust mapping API (like Google Maps) would allow for better visualization of property locations and nearby amenities.</p>
+ <button onclick="alert('Acknowledged: Investigating advanced mapping integration.')">Mark as Planned</button>
+ </div>
+
+ <div class="suggestion">
+ <h2>Virtual Tours</h2>
+ <p>Adding virtual tour functionality (360° views or video walkthroughs) would allow potential renters to explore properties remotely and reduce the need for in-person visits.</p>
+ <button onclick="alert('Acknowledged: Exploring Virtual Tour Implementation.')">Mark as Planned</button>
+ </div>
+
+ <div class="suggestion">
+ <h2>User Reviews & Ratings</h2>
+ <p>Implementing a user review system would provide valuable social proof and help renters make informed decisions.</p>
+ <button onclick="alert('Acknowledged: Considering User Review System.')">Mark as Planned</button>
+ </div>
+
+ <div class="suggestion">
+ <h2>Mobile Responsiveness Improvements</h2>
+ <p>While generally responsive, certain elements on smaller screens need further optimization for improved usability.</p>
+ <button onclick="alert('Acknowledged: Prioritizing Mobile Responsiveness Tweaks.')">Mark as Planned</button>
+ </div>
+
+ <div class="suggestion">
+ <h2>Automated Rent Estimate</h2>
+ <p>Provide a rent estimate based on location and property characteristics. This is useful for renters who are still exploring options.</p>
+ <button onclick="alert('Acknowledged: Investigating Automated Rent Estimate Feature.')">Mark as Planned</button>
+ </div>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f83743a0fa7e/gemma3-12b.png b/data/artifacts/f83743a0fa7e/gemma3-12b.png
new file mode 100644
index 0000000..72c5a99
Binary files /dev/null and b/data/artifacts/f83743a0fa7e/gemma3-12b.png differ
diff --git a/data/artifacts/f83743a0fa7e/grok.html b/data/artifacts/f83743a0fa7e/grok.html
new file mode 100644
index 0000000..4547bb3
--- /dev/null
+++ b/data/artifacts/f83743a0fa7e/grok.html
@@ -0,0 +1,732 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Rentv.com — Suggested Upgrades</title>
+<style>
+:root {
+ --bg: #0b0f1a;
+ --bg2: #121829;
+ --card: #161e30;
+ --card-hover: #1c2640;
+ --border: #243049;
+ --accent: #6c5ce7;
+ --accent2: #a29bfe;
+ --accent-glow: rgba(108, 92, 231, 0.35);
+ --teal: #00cec9;
+ --gold: #fdcb6e;
+ --rose: #ff7675;
+ --text: #e8ecf4;
+ --muted: #8892a8;
+ --success: #55efc4;
+ --radius: 16px;
+ --shadow: 0 12px 40px rgba(0,0,0,0.45);
+}
+* { box-sizing: border-box; margin: 0; padding: 0; }
+html { scroll-behavior: smooth; }
+body {
+ font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
+ background: var(--bg);
+ color: var(--text);
+ min-height: 100vh;
+ line-height: 1.5;
+ overflow-x: hidden;
+}
+body::before {
+ content: "";
+ position: fixed;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 80% 50% at 20% -10%, rgba(108,92,231,0.25), transparent 50%),
+ radial-gradient(ellipse 60% 40% at 90% 10%, rgba(0,206,201,0.12), transparent 45%),
+ radial-gradient(ellipse 50% 30% at 50% 100%, rgba(162,155,254,0.08), transparent 50%);
+ pointer-events: none;
+ z-index: 0;
+}
+.wrap {
+ position: relative;
+ z-index: 1;
+ max-width: 1100px;
+ margin: 0 auto;
+ padding: 28px 20px 60px;
+}
+/* Header */
+header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ flex-wrap: wrap;
+ margin-bottom: 36px;
+ padding-bottom: 20px;
+ border-bottom: 1px solid var(--border);
+}
+.logo {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ text-decoration: none;
+ color: inherit;
+}
+.logo-mark {
+ width: 42px;
+ height: 42px;
+ border-radius: 12px;
+ background: linear-gradient(135deg, var(--accent), var(--teal));
+ display: grid;
+ place-items: center;
+ font-weight: 800;
+ font-size: 18px;
+ box-shadow: 0 0 24px var(--accent-glow);
+}
+.logo-text {
+ font-size: 1.35rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+}
+.logo-text span { color: var(--accent2); }
+.badge-live {
+ font-size: 0.7rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ background: rgba(85,239,196,0.12);
+ color: var(--success);
+ border: 1px solid rgba(85,239,196,0.3);
+ padding: 4px 10px;
+ border-radius: 999px;
+}
+/* Hero */
+.hero {
+ text-align: center;
+ margin-bottom: 40px;
+}
+.hero h1 {
+ font-size: clamp(1.75rem, 4vw, 2.4rem);
+ font-weight: 800;
+ letter-spacing: -0.03em;
+ margin-bottom: 10px;
+ background: linear-gradient(135deg, #fff 30%, var(--accent2));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+.hero p {
+ color: var(--muted);
+ max-width: 520px;
+ margin: 0 auto 22px;
+ font-size: 1.05rem;
+}
+.plan-chip {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: var(--card);
+ border: 1px solid var(--border);
+ padding: 8px 16px;
+ border-radius: 999px;
+ font-size: 0.9rem;
+}
+.plan-chip strong { color: var(--gold); }
+/* Filters */
+.filters {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ justify-content: center;
+ margin-bottom: 28px;
+}
+.filters button {
+ background: transparent;
+ border: 1px solid var(--border);
+ color: var(--muted);
+ padding: 8px 16px;
+ border-radius: 999px;
+ cursor: pointer;
+ font-size: 0.85rem;
+ font-weight: 600;
+ transition: all 0.2s;
+}
+.filters button:hover { border-color: var(--accent2); color: var(--text); }
+.filters button.active {
+ background: var(--accent);
+ border-color: var(--accent);
+ color: #fff;
+ box-shadow: 0 4px 16px var(--accent-glow);
+}
+/* Grid */
+.grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 18px;
+ margin-bottom: 36px;
+}
+.card {
+ background: var(--card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 22px;
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ transition: transform 0.2s, background 0.2s, border-color 0.2s, box-shadow 0.2s;
+ position: relative;
+ overflow: hidden;
+}
+.card::after {
+ content: "";
+ position: absolute;
+ top: 0; left: 0; right: 0;
+ height: 3px;
+ background: linear-gradient(90deg, var(--accent), var(--teal));
+ opacity: 0;
+ transition: opacity 0.2s;
+}
+.card:hover {
+ transform: translateY(-4px);
+ background: var(--card-hover);
+ border-color: rgba(108,92,231,0.45);
+ box-shadow: var(--shadow);
+}
+.card:hover::after { opacity: 1; }
+.card.featured {
+ border-color: rgba(108,92,231,0.55);
+ box-shadow: 0 0 0 1px rgba(108,92,231,0.2), var(--shadow);
+}
+.card.featured::after { opacity: 1; }
+.card-top {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 10px;
+}
+.icon {
+ width: 48px;
+ height: 48px;
+ border-radius: 14px;
+ display: grid;
+ place-items: center;
+ font-size: 1.4rem;
+ flex-shrink: 0;
+}
+.icon.purple { background: rgba(108,92,231,0.2); }
+.icon.teal { background: rgba(0,206,201,0.18); }
+.icon.gold { background: rgba(253,203,110,0.18); }
+.icon.rose { background: rgba(255,118,117,0.18); }
+.tag {
+ font-size: 0.68rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ padding: 4px 9px;
+ border-radius: 6px;
+ background: rgba(108,92,231,0.2);
+ color: var(--accent2);
+}
+.tag.best { background: rgba(253,203,110,0.2); color: var(--gold); }
+.tag.new { background: rgba(0,206,201,0.18); color: var(--teal); }
+.card h3 {
+ font-size: 1.15rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+}
+.card .desc {
+ color: var(--muted);
+ font-size: 0.92rem;
+ flex: 1;
+}
+.perks {
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+.perks li {
+ font-size: 0.85rem;
+ color: var(--text);
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.perks li::before {
+ content: "✓";
+ color: var(--success);
+ font-weight: 700;
+ font-size: 0.8rem;
+}
+.price-row {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 10px;
+ padding-top: 8px;
+ border-top: 1px solid var(--border);
+}
+.price {
+ font-size: 1.45rem;
+ font-weight: 800;
+ letter-spacing: -0.03em;
+}
+.price small {
+ font-size: 0.75rem;
+ font-weight: 500;
+ color: var(--muted);
+}
+.was {
+ font-size: 0.8rem;
+ color: var(--muted);
+ text-decoration: line-through;
+}
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ width: 100%;
+ padding: 12px 18px;
+ border: none;
+ border-radius: 12px;
+ font-weight: 700;
+ font-size: 0.92rem;
+ cursor: pointer;
+ transition: transform 0.15s, box-shadow 0.15s, background 0.15s;
+}
+.btn-primary {
+ background: linear-gradient(135deg, var(--accent), #5a4bd1);
+ color: #fff;
+ box-shadow: 0 6px 20px var(--accent-glow);
+}
+.btn-primary:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 10px 28px var(--accent-glow);
+}
+.btn-primary:active { transform: translateY(0); }
+.btn-primary.added {
+ background: linear-gradient(135deg, #00b894, #00cec9);
+ box-shadow: 0 6px 20px rgba(0,206,201,0.3);
+}
+.btn-ghost {
+ background: transparent;
+ border: 1px solid var(--border);
+ color: var(--text);
+}
+.btn-ghost:hover { border-color: var(--accent2); background: rgba(108,92,231,0.08); }
+/* Summary bar */
+.summary {
+ position: sticky;
+ bottom: 16px;
+ background: rgba(18, 24, 41, 0.92);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ border: 1px solid var(--border);
+ border-radius: 18px;
+ padding: 16px 20px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ flex-wrap: wrap;
+ box-shadow: var(--shadow);
+}
+.summary-info { display: flex; flex-direction: column; gap: 2px; }
+.summary-info .label { font-size: 0.8rem; color: var(--muted); }
+.summary-info .total {
+ font-size: 1.25rem;
+ font-weight: 800;
+}
+.summary-info .total span { color: var(--accent2); }
+.summary .actions { display: flex; gap: 10px; flex-wrap: wrap; }
+/* Toast */
+.toast {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ background: var(--card);
+ border: 1px solid rgba(85,239,196,0.4);
+ color: var(--text);
+ padding: 14px 18px;
+ border-radius: 12px;
+ box-shadow: var(--shadow);
+ transform: translateX(120%);
+ transition: transform 0.35s cubic-bezier(0.22,1,0.36,1);
+ z-index: 100;
+ font-size: 0.9rem;
+ max-width: 300px;
+}
+.toast.show { transform: translateX(0); }
+.toast strong { color: var(--success); }
+/* Comparison strip */
+.compare {
+ background: var(--bg2);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 22px;
+ margin-bottom: 28px;
+}
+.compare h2 {
+ font-size: 1rem;
+ font-weight: 700;
+ margin-bottom: 14px;
+ color: var(--muted);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+}
+.table-wrap { overflow-x: auto; }
+table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.88rem;
+}
+th, td {
+ text-align: left;
+ padding: 10px 12px;
+ border-bottom: 1px solid var(--border);
+}
+th { color: var(--muted); font-weight: 600; }
+td:first-child { color: var(--muted); }
+.yes { color: var(--success); font-weight: 700; }
+.no { color: #5a6478; }
+/* Footer note */
+.note {
+ text-align: center;
+ color: var(--muted);
+ font-size: 0.8rem;
+ margin-top: 24px;
+}
+@media (max-width: 560px) {
+ .summary { flex-direction: column; align-items: stretch; text-align: center; }
+ .summary .actions { justify-content: center; }
+ .summary .actions .btn { flex: 1; }
+}
+</style>
+</head>
+<body>
+<div class="wrap">
+ <header>
+ <a class="logo" href="#" onclick="return false;">
+ <div class="logo-mark">R</div>
+ <div class="logo-text">Ren<span>tv</span>.com</div>
+ </a>
+ <div class="badge-live">Personalized picks</div>
+ </header>
+
+ <section class="hero">
+ <h1>Suggested upgrades for you</h1>
+ <p>Based on your viewing habits and current Basic plan, these upgrades unlock more screens, 4K, and premium channels.</p>
+ <div class="plan-chip">Current plan: <strong>Basic · $9.99/mo</strong> · 1 screen · HD</div>
+ </section>
+
+ <div class="filters" id="filters">
+ <button class="active" data-filter="all">All</button>
+ <button data-filter="plan">Plans</button>
+ <button data-filter="addon">Add-ons</button>
+ <button data-filter="device">Devices</button>
+ <button data-filter="bundle">Bundles</button>
+ </div>
+
+ <div class="grid" id="grid"></div>
+
+ <section class="compare">
+ <h2>Plan comparison</h2>
+ <div class="table-wrap">
+ <table>
+ <thead>
+ <tr>
+ <th>Feature</th>
+ <th>Basic</th>
+ <th>Standard</th>
+ <th>Premium</th>
+ <th>Family Ultra</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td>Monthly price</td><td>$9.99</td><td>$15.99</td><td>$22.99</td><td>$29.99</td></tr>
+ <tr><td>Simultaneous screens</td><td>1</td><td>2</td><td>4</td><td>6</td></tr>
+ <tr><td>Max quality</td><td>HD</td><td>Full HD</td><td>4K HDR</td><td>4K HDR + Atmos</td></tr>
+ <tr><td>Downloads</td><td class="no">—</td><td class="yes">✓</td><td class="yes">✓</td><td class="yes">✓</td></tr>
+ <tr><td>Premium live channels</td><td class="no">—</td><td class="no">—</td><td class="yes">✓</td><td class="yes">✓</td></tr>
+ <tr><td>Kids profiles + controls</td><td class="no">—</td><td class="yes">✓</td><td class="yes">✓</td><td class="yes">✓</td></tr>
+ <tr><td>Early access premieres</td><td class="no">—</td><td class="no">—</td><td class="no">—</td><td class="yes">✓</td></tr>
+ </tbody>
+ </table>
+ </div>
+ </section>
+
+ <div class="summary" id="summary">
+ <div class="summary-info">
+ <span class="label">Selected upgrades</span>
+ <div class="total"><span id="selCount">0</span> items · +$<span id="selTotal">0.00</span>/mo</div>
+ </div>
+ <div class="actions">
+ <button class="btn btn-ghost" id="btnClear" type="button">Clear</button>
+ <button class="btn btn-primary" id="btnApply" type="button">Apply upgrades</button>
+ </div>
+ </div>
+
+ <p class="note">Prices billed monthly. Cancel or change anytime. Taxes may apply. Rentv.com demo UI — no charges processed.</p>
+</div>
+
+<div class="toast" id="toast" role="status"></div>
+
+<script>
+(function () {
+ const upgrades = [
+ {
+ id: "std",
+ cat: "plan",
+ icon: "📺",
+ iconClass: "purple",
+ tag: "Popular",
+ tagClass: "",
+ featured: true,
+ title: "Standard Plan",
+ desc: "Perfect step up: second screen for the living room and Full HD for sports & movies.",
+ perks: ["2 simultaneous screens", "Full HD streaming", "Downloads on 2 devices", "Kids profile"],
+ price: 6,
+ was: null,
+ label: "+$6.00/mo"
+ },
+ {
+ id: "prem",
+ cat: "plan",
+ icon: "✨",
+ iconClass: "gold",
+ tag: "Best value",
+ tagClass: "best",
+ featured: true,
+ title: "Premium Plan",
+ desc: "4K HDR on every compatible display plus four screens and full premium live lineup.",
+ perks: ["4 screens · 4K HDR", "Premium live channels", "Spatial audio support", "Priority support"],
+ price: 13,
+ was: 15,
+ label: "+$13.00/mo"
+ },
+ {
+ id: "family",
+ cat: "plan",
+ icon: "👨👩👧👦",
+ iconClass: "teal",
+ tag: "New",
+ tagClass: "new",
+ featured: false,
+ title: "Family Ultra",
+ desc: "Six screens, Atmos, and early premieres — built for large households and sharers.",
+ perks: ["6 screens", "4K HDR + Atmos", "Early access premieres", "Advanced parental controls"],
+ price: 20,
+ was: null,
+ label: "+$20.00/mo"
+ },
+ {
+ id: "sports",
+ cat: "addon",
+ icon: "🏆",
+ iconClass: "rose",
+ tag: "Add-on",
+ tagClass: "",
+ featured: false,
+ title: "Sports Plus Pack",
+ desc: "Live leagues, multiview, and exclusive studio shows. Pairs with any plan above Basic.",
+ perks: ["40+ live sports channels", "Multiview up to 4 games", "In-game stats overlay", "7-day replay"],
+ price: 7.99,
+ was: 9.99,
+ label: "+$7.99/mo"
+ },
+ {
+ id: "cinema",
+ cat: "addon",
+ icon: "🎬",
+ iconClass: "purple",
+ tag: "Add-on",
+ tagClass: "",
+ featured: false,
+ title: "Cinema Vault",
+ desc: "Blockbuster premieres and exclusive originals 30 days before general catalog release.",
+ perks: ["Same-week theatrical hits", "Director’s cuts library", "Offline cinema downloads", "No ads on Vault titles"],
+ price: 5.99,
+ was: null,
+ label: "+$5.99/mo"
+ },
+ {
+ id: "4kstick",
+ cat: "device",
+ icon: "📡",
+ iconClass: "teal",
+ tag: "Device",
+ tagClass: "new",
+ featured: false,
+ title: "Rentv 4K Stick",
+ desc: "Plug-in streaming stick optimized for Rentv — voice remote, Wi‑Fi 6, Dolby Vision.",
+ perks: ["Dolby Vision & Atmos", "Voice remote included", "One-tap Rentv home", "Free shipping"],
+ price: 3.99,
+ was: null,
+ label: "+$3.99/mo lease"
+ },
+ {
+ id: "soundbar",
+ cat: "device",
+ icon: "🔊",
+ iconClass: "gold",
+ tag: "Device",
+ tagClass: "",
+ featured: false,
+ title: "Compact Soundbar",
+ desc: "Rent-to-own soundbar tuned for dialogue clarity and night mode — ideal for apartments.",
+ perks: ["HDMI eARC", "Dialogue boost", "App EQ presets", "Swap anytime"],
+ price: 8.5,
+ was: 11,
+ label: "+$8.50/mo lease"
+ },
+ {
+ id: "bundle",
+ cat: "bundle",
+ icon: "🎁",
+ iconClass: "rose",
+ tag: "Bundle",
+ tagClass: "best",
+ featured: true,
+ title: "Premium + Sports Bundle",
+ desc: "Lock Premium plan with Sports Plus at a discounted combo rate. Most-watched upgrade path.",
+ perks: ["Everything in Premium", "Full Sports Plus Pack", "Save $4/mo vs separate", "Cancel bundle anytime"],
+ price: 16.99,
+ was: 20.99,
+ label: "+$16.99/mo"
+ },
+ {
+ id: "student",
+ cat: "bundle",
+ icon: "🎓",
+ iconClass: "purple",
+ tag: "Bundle",
+ tagClass: "",
+ featured: false,
+ title: "Student Duo",
+ desc: "Standard plan + Cinema Vault with verified student pricing for 12 months.",
+ perks: ["2 screens Full HD", "Cinema Vault included", "Student ID verify once", "Auto-remind before renew"],
+ price: 9.49,
+ was: 12.98,
+ label: "+$9.49/mo"
+ }
+ ];
+
+ const selected = new Set();
+ const grid = document.getElementById("grid");
+ const toast = document.getElementById("toast");
+ let toastTimer = null;
+ let activeFilter = "all";
+
+ function money(n) {
+ return n.toFixed(2);
+ }
+
+ function showToast(msg) {
+ toast.innerHTML = msg;
+ toast.classList.add("show");
+ clearTimeout(toastTimer);
+ toastTimer = setTimeout(function () {
+ toast.classList.remove("show");
+ }, 2600);
+ }
+
+ function updateSummary() {
+ let total = 0;
+ selected.forEach(function (id) {
+ const u = upgrades.find(function (x) { return x.id === id; });
+ if (u) total += u.price;
+ });
+ document.getElementById("selCount").textContent = String(selected.size);
+ document.getElementById("selTotal").textContent = money(total);
+ }
+
+ function render() {
+ grid.innerHTML = "";
+ const list = upgrades.filter(function (u) {
+ return activeFilter === "all" || u.cat === activeFilter;
+ });
+ list.forEach(function (u) {
+ const isOn = selected.has(u.id);
+ const card = document.createElement("article");
+ card.className = "card" + (u.featured ? " featured" : "");
+ card.dataset.cat = u.cat;
+ card.innerHTML =
+ '<div class="card-top">' +
+ '<div class="icon ' + u.iconClass + '">' + u.icon + "</div>" +
+ '<span class="tag ' + u.tagClass + '">' + u.tag + "</span>" +
+ "</div>" +
+ "<h3>" + u.title + "</h3>" +
+ '<p class="desc">' + u.desc + "</p>" +
+ '<ul class="perks">' +
+ u.perks.map(function (p) { return "<li>" + p + "</li>"; }).join("") +
+ "</ul>" +
+ '<div class="price-row">' +
+ '<div class="price">' + u.label +
+ (u.was ? ' <span class="was">was +$' + money(u.was) + "</span>" : "") +
+ "</div>" +
+ "</div>" +
+ '<button type="button" class="btn btn-primary' + (isOn ? " added" : "") + '" data-id="' + u.id + '">' +
+ (isOn ? "✓ Added" : "Add upgrade") +
+ "</button>";
+ grid.appendChild(card);
+ });
+ }
+
+ grid.addEventListener("click", function (e) {
+ const btn = e.target.closest("button[data-id]");
+ if (!btn) return;
+ const id = btn.getAttribute("data-id");
+ const u = upgrades.find(function (x) { return x.id === id; });
+ if (!u) return;
+ if (selected.has(id)) {
+ selected.delete(id);
+ showToast("Removed <strong>" + u.title + "</strong>");
+ } else {
+ selected.add(id);
+ showToast("Added <strong>" + u.title + "</strong> · +$" + money(u.price) + "/mo");
+ }
+ render();
+ updateSummary();
+ });
+
+ document.getElementById("filters").addEventListener("click", function (e) {
+ const b = e.target.closest("button[data-filter]");
+ if (!b) return;
+ activeFilter = b.getAttribute("data-filter");
+ document.querySelectorAll("#filters button").forEach(function (el) {
+ el.classList.toggle("active", el === b);
+ });
+ render();
+ });
+
+ document.getElementById("btnClear").addEventListener("click", function () {
+ selected.clear();
+ render();
+ updateSummary();
+ showToast("Selection cleared");
+ });
+
+ document.getElementById("btnApply").addEventListener("click", function () {
+ if (selected.size === 0) {
+ showToast("Pick at least one upgrade first");
+ return;
+ }
+ let total = 0;
+ const names = [];
+ selected.forEach(function (id) {
+ const u = upgrades.find(function (x) { return x.id === id; });
+ if (u) {
+ total += u.price;
+ names.push(u.title);
+ }
+ });
+ showToast(
+ "<strong>Upgrades queued!</strong><br>" +
+ names.length + " item(s) · new total add-on $" + money(total) + "/mo"
+ );
+ });
+
+ render();
+ updateSummary();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f83743a0fa7e/grok.png b/data/artifacts/f83743a0fa7e/grok.png
new file mode 100644
index 0000000..f548763
Binary files /dev/null and b/data/artifacts/f83743a0fa7e/grok.png differ
diff --git a/data/artifacts/f83743a0fa7e/qwen25-7b.html b/data/artifacts/f83743a0fa7e/qwen25-7b.html
new file mode 100644
index 0000000..31a0109
--- /dev/null
+++ b/data/artifacts/f83743a0fa7e/qwen25-7b.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Rentv.com Upgrade Suggestions</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ margin: 20px;
+ }
+ h1 {
+ color: #333;
+ }
+ ul {
+ list-style-type: disc;
+ padding-left: 20px;
+ }
+ li {
+ margin-bottom: 5px;
+ }
+ </style>
+</head>
+<body>
+ <h1>Rentv.com Upgrade Suggestions</h1>
+ <ul>
+ <li>Implement a responsive design to ensure the website looks good on all devices.</li>
+ <li>Optimize video streaming by compressing files and using adaptive bitrate streaming.</li>
+ <li>Add user reviews and ratings for movies and TV shows to enhance community engagement.</li>
+ <li>Integrate social media sharing buttons to allow users to share content easily.</li>
+ <li>Enhance search functionality with advanced filters and better auto-suggestions.</li>
+ </ul>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f83743a0fa7e/qwen25-7b.png b/data/artifacts/f83743a0fa7e/qwen25-7b.png
new file mode 100644
index 0000000..a426bb9
Binary files /dev/null and b/data/artifacts/f83743a0fa7e/qwen25-7b.png differ
diff --git a/data/artifacts/f83743a0fa7e/qwen3-14b.html b/data/artifacts/f83743a0fa7e/qwen3-14b.html
new file mode 100644
index 0000000..915e20b
--- /dev/null
+++ b/data/artifacts/f83743a0fa7e/qwen3-14b.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Rentv.com Suggested Upgrades</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ margin: 0;
+ padding: 0;
+ background-color: #f4f4f4;
+ color: #333;
+ }
+
+ header {
+ background-color: #007bff;
+ color: white;
+ padding: 20px;
+ text-align: center;
+ }
+
+ main {
+ padding: 20px;
+ }
+
+ h2 {
+ color: #007bff;
+ }
+
+ .upgrade {
+ background-color: white;
+ border: 1px solid #ddd;
+ padding: 15px;
+ margin-bottom: 15px;
+ border-radius: 5px;
+ }
+
+ .upgrade h3 {
+ margin-top: 0;
+ color: #0056b3;
+ }
+
+ .upgrade p {
+ margin: 5px 0;
+ }
+
+ footer {
+ text-align: center;
+ padding: 10px;
+ background-color: #e9ecef;
+ margin-top: 20px;
+ font-size: 0.9em;
+ color: #666;
+ }
+ </style>
+</head>
+<body>
+ <header>
+ <h1>Rentv.com Suggested Upgrades</h1>
+ </header>
+ <main>
+ <div class="upgrade">
+ <h3>Upgrade 1: High-Resolution Video Streaming</h3>
+ <p>Enhance the viewer experience with 4K and HDR support for all videos on the platform.</p>
+ <p>Implementation: Integrate adaptive streaming protocols (e.g., HLS or DASH) and optimize server infrastructure for higher bandwidth usage.</p>
+ </div>
+ <div class="upgrade">
+ <h3>Upgrade 2: AI-Powered Content Recommendations</h3>
+ <p>Use machine learning algorithms to suggest personalized video content based on user behavior and preferences.</p>
+ <p>Implementation: Deploy recommendation engines that analyze watch history, search patterns, and user ratings.</p>
+ </div>
+ <div class="upgrade">
+ <h3>Upgrade 3: Interactive Video Features</h3>
+ <p>Introduce interactive elements such as branching storylines, polls, and hotspots for a more engaging experience.</p>
+ <p>Implementation: Use web technologies like WebVTT and JavaScript to enable interactive video features within the player.</p>
+ </div>
+ <div class="upgrade">
+ <h3>Upgrade 4: Mobile App Optimization</h3>
+ <p>Ensure the Rentv.com experience is seamless on mobile devices, with faster load times and optimized UI.</p>
+ <p>Implementation: Use responsive design, reduce image sizes, and leverage caching strategies for mobile users.</p>
+ </div>
+ <div class="upgrade">
+ <h3>Upgrade 5: Enhanced Security Measures</h3>
+ <p>Implement advanced security features to protect user data and prevent unauthorized access.</p>
+ <p>Implementation: Use HTTPS, regular security audits, and encryption for all user data.</p>
+ </div>
+ </main>
+ <footer>
+ © 2025 Rentv.com. All rights reserved.
+ </footer>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f83743a0fa7e/qwen3-14b.png b/data/artifacts/f83743a0fa7e/qwen3-14b.png
new file mode 100644
index 0000000..79f2f84
Binary files /dev/null and b/data/artifacts/f83743a0fa7e/qwen3-14b.png differ
diff --git a/data/challenges.json b/data/challenges.json
index 21641a4..7df7633 100644
--- a/data/challenges.json
+++ b/data/challenges.json
@@ -6135,7 +6135,7 @@
"category": "Custom",
"designTools": false,
"created_at": "2026-07-23T17:17:55.835Z",
- "winner": null,
+ "winner": "claude-code",
"runs": [
{
"model": "qwen3-14b",
@@ -6147,7 +6147,14 @@
"finished_at": "2026-07-23T17:18:20.812Z",
"queued_at": "2026-07-23T17:17:55.840Z",
"bytes": 3474,
- "thumb": true
+ "thumb": true,
+ "aiScore": 3.5,
+ "aiReason": "The page is interactive and fulfills the challenge requirements but lacks visual appeal due to a monochromatic background.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 0
+ },
+ "aiSpread": 7
},
{
"model": "gemma3-12b",
@@ -6170,7 +6177,14 @@
"finished_at": "2026-07-23T17:19:21.214Z",
"queued_at": "2026-07-23T17:17:55.847Z",
"bytes": 8846,
- "thumb": true
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is interactive and fulfills the challenge requirements but lacks visual appeal.",
+ "aiScores": {
+ "qwen2.5vl:7b": 6,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
},
{
"model": "qwen25-7b",
@@ -6182,17 +6196,33 @@
"finished_at": "2026-07-23T17:18:11.068Z",
"queued_at": "2026-07-23T17:17:55.854Z",
"bytes": 2278,
- "thumb": true
+ "thumb": true,
+ "aiScore": 5.8,
+ "aiReason": "The page is blank and does not display any interactive Voronoi shatter effect.",
+ "aiScores": {
+ "qwen2.5vl:7b": 4,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 3.5
},
{
"model": "hf-qwen-coder-32b",
- "status": "running",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
+ "seconds": 73,
+ "cost": 0,
"started_at": "2026-07-23T17:19:21.219Z",
- "finished_at": null,
- "queued_at": "2026-07-23T17:17:55.858Z"
+ "finished_at": "2026-07-23T17:20:34.369Z",
+ "queued_at": "2026-07-23T17:17:55.858Z",
+ "bytes": 5265,
+ "thumb": true,
+ "aiScore": 5.3,
+ "aiReason": "The page is empty and does not display any interactive Voronoi shatter effect or points.",
+ "aiScores": {
+ "qwen2.5vl:7b": 4,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 2.5
},
{
"model": "claude-code",
@@ -6204,17 +6234,33 @@
"finished_at": "2026-07-23T17:19:28.376Z",
"queued_at": "2026-07-23T17:17:55.862Z",
"bytes": 8756,
- "thumb": true
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The model fulfills the challenge requirements and has an appealing visual quality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
},
{
"model": "kimi",
- "status": "running",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
+ "seconds": 171,
+ "cost": 0.0207,
"started_at": "2026-07-23T17:17:55.883Z",
- "finished_at": null,
- "queued_at": "2026-07-23T17:17:55.865Z"
+ "finished_at": "2026-07-23T17:20:46.919Z",
+ "queued_at": "2026-07-23T17:17:55.865Z",
+ "bytes": 7959,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The model fulfills the challenge requirements and has good visual quality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
},
{
"model": "gpt",
@@ -6226,7 +6272,14 @@
"finished_at": "2026-07-23T17:18:22.582Z",
"queued_at": "2026-07-23T17:17:55.868Z",
"bytes": 9299,
- "thumb": true
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The model fulfills the challenge requirements and has good visual quality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
},
{
"model": "grok",
@@ -6238,6 +6291,1168 @@
"finished_at": "2026-07-23T17:19:05.291Z",
"queued_at": "2026-07-23T17:17:55.871Z",
"bytes": 15551,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The model successfully implements the Voronoi shatter effect with clear instructions and smooth interaction.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T17:21:26.904Z",
+ "aiPick": "claude-code",
+ "voted_at": "2026-07-23T17:21:36.681Z"
+ },
+ {
+ "id": "673cf6b31c78",
+ "title": "All-Models — Snake Reimagined",
+ "prompt": "Build a single-file HTML page: a polished modern take on Snake. Arrow/WASD controls, smooth movement, growing tail, food, score, increasing speed, game-over + restart. Neon glow aesthetic, sound-optional. Self-contained, no external libraries.",
+ "category": "Games",
+ "designTools": false,
+ "created_at": "2026-07-23T17:21:51.111Z",
+ "winner": "gpt",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 231,
+ "cost": 0,
+ "started_at": "2026-07-23T17:21:51.159Z",
+ "finished_at": "2026-07-23T17:25:42.201Z",
+ "queued_at": "2026-07-23T17:21:51.124Z",
+ "bytes": 22355,
+ "thumb": true,
+ "aiScore": 5.5,
+ "aiReason": "The page is visually appealing and functional but lacks some elements like sound effects or increasing speed.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 4
+ },
+ "aiSpread": 3
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 42,
+ "cost": 0,
+ "started_at": "2026-07-23T17:25:42.205Z",
+ "finished_at": "2026-07-23T17:26:23.790Z",
+ "queued_at": "2026-07-23T17:21:51.131Z",
+ "bytes": 5745,
+ "thumb": true,
+ "aiScore": 7.8,
+ "aiReason": "The game fulfills all requirements and has a clean neon aesthetic.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 30,
+ "cost": 0,
+ "started_at": "2026-07-23T17:26:23.793Z",
+ "finished_at": "2026-07-23T17:26:54.043Z",
+ "queued_at": "2026-07-23T17:21:51.137Z",
+ "bytes": 5797,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is functional but lacks the neon glow aesthetic and sound effects.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 22,
+ "cost": 0,
+ "started_at": "2026-07-23T17:21:51.161Z",
+ "finished_at": "2026-07-23T17:22:13.231Z",
+ "queued_at": "2026-07-23T17:21:51.141Z",
+ "bytes": 3831,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is functional but lacks the neon glow aesthetic and sound effects.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 43,
+ "cost": 0,
+ "started_at": "2026-07-23T17:26:54.047Z",
+ "finished_at": "2026-07-23T17:27:37.229Z",
+ "queued_at": "2026-07-23T17:21:51.145Z",
+ "bytes": 3187,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The game is functional but lacks some visual polish and features like sound.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 94,
+ "cost": 0,
+ "started_at": "2026-07-23T17:21:51.164Z",
+ "finished_at": "2026-07-23T17:23:25.664Z",
+ "queued_at": "2026-07-23T17:21:51.149Z",
+ "bytes": 15703,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The game is visually appealing and fulfills all the requirements of the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 168,
+ "cost": 0.0199,
+ "started_at": "2026-07-23T17:21:51.167Z",
+ "finished_at": "2026-07-23T17:24:38.699Z",
+ "queued_at": "2026-07-23T17:21:51.151Z",
+ "bytes": 10655,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The game is functional and visually appealing but lacks some features like sound effects.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 96,
+ "cost": 0.1485,
+ "started_at": "2026-07-23T17:21:51.169Z",
+ "finished_at": "2026-07-23T17:23:27.473Z",
+ "queued_at": "2026-07-23T17:21:51.154Z",
+ "bytes": 38103,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The game fulfills the challenge requirements and has a visually appealing neon aesthetic.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 72,
+ "cost": 0.0977,
+ "started_at": "2026-07-23T17:21:51.171Z",
+ "finished_at": "2026-07-23T17:23:03.018Z",
+ "queued_at": "2026-07-23T17:21:51.156Z",
+ "bytes": 17972,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The game fulfills all requirements and has a visually appealing neon aesthetic.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T17:28:34.931Z",
+ "aiPick": "gpt",
+ "voted_at": "2026-07-23T17:28:42.553Z"
+ },
+ {
+ "id": "9ba7a5497a50",
+ "title": "All-Models — Clock Gallery",
+ "prompt": "Build a single-file HTML page: a gallery of 4 live clocks side by side — an analog clock, a flip-clock, a binary clock, and a word clock (IT IS HALF PAST TEN). All tick in real time, clean typography, dark theme. Self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T17:28:58.481Z",
+ "winner": "gpt",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 47,
+ "cost": 0,
+ "started_at": "2026-07-23T17:28:58.513Z",
+ "finished_at": "2026-07-23T17:29:45.627Z",
+ "queued_at": "2026-07-23T17:28:58.485Z",
+ "bytes": 6981,
+ "thumb": true,
+ "aiScore": 9,
+ "aiReason": "The page is clean and fulfills all requirements of the challenge, including real-time functionality and a dark theme.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 36,
+ "cost": 0,
+ "started_at": "2026-07-23T17:29:45.632Z",
+ "finished_at": "2026-07-23T17:30:21.559Z",
+ "queued_at": "2026-07-23T17:28:58.490Z",
+ "bytes": 5425,
+ "thumb": true,
+ "aiScore": 9,
+ "aiReason": "The model successfully creates all four types of clocks as specified and maintains a clean dark theme.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 19,
+ "cost": 0,
+ "started_at": "2026-07-23T17:30:21.564Z",
+ "finished_at": "2026-07-23T17:30:41.021Z",
+ "queued_at": "2026-07-23T17:28:58.494Z",
+ "bytes": 5890,
+ "thumb": true,
+ "aiScore": 7,
+ "aiReason": "The clocks are present and functional, but the design is minimalistic without additional visual elements or typography.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 30,
+ "cost": 0,
+ "started_at": "2026-07-23T17:28:58.515Z",
+ "finished_at": "2026-07-23T17:29:28.679Z",
+ "queued_at": "2026-07-23T17:28:58.497Z",
+ "bytes": 5094,
+ "thumb": true,
+ "aiScore": 9,
+ "aiReason": "The image meets the challenge criteria and has clean typography and a dark theme.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 74,
+ "cost": 0,
+ "started_at": "2026-07-23T17:30:41.025Z",
+ "finished_at": "2026-07-23T17:31:54.559Z",
+ "queued_at": "2026-07-23T17:28:58.500Z",
+ "bytes": 5519,
+ "thumb": true,
+ "aiScore": 9,
+ "aiReason": "The page meets all the requirements and has clean typography with a dark theme.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 106,
+ "cost": 0,
+ "started_at": "2026-07-23T17:28:58.516Z",
+ "finished_at": "2026-07-23T17:30:44.343Z",
+ "queued_at": "2026-07-23T17:28:58.503Z",
+ "bytes": 11815,
+ "thumb": true,
+ "aiScore": 9,
+ "aiReason": "The page fulfills the challenge requirements and has clean typography in a dark theme.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 254,
+ "cost": 0.0327,
+ "started_at": "2026-07-23T17:28:58.521Z",
+ "finished_at": "2026-07-23T17:33:12.812Z",
+ "queued_at": "2026-07-23T17:28:58.506Z",
+ "bytes": 12913,
+ "thumb": true,
+ "aiScore": 9,
+ "aiReason": "The model successfully creates four live clocks side by side in a dark theme with clean typography and real-time functionality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 76,
+ "cost": 0.0982,
+ "started_at": "2026-07-23T17:28:58.523Z",
+ "finished_at": "2026-07-23T17:30:14.110Z",
+ "queued_at": "2026-07-23T17:28:58.508Z",
+ "bytes": 22015,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The gallery meets the challenge criteria and has clean typography with a dark theme.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 70,
+ "cost": 0.0947,
+ "started_at": "2026-07-23T17:28:58.525Z",
+ "finished_at": "2026-07-23T17:30:08.036Z",
+ "queued_at": "2026-07-23T17:28:58.511Z",
+ "bytes": 17604,
+ "thumb": true,
+ "aiScore": 9,
+ "aiReason": "The gallery meets the challenge criteria and has clean typography in a dark theme.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9
+ },
+ "aiSpread": 0
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T17:33:58.975Z",
+ "aiPick": "gpt",
+ "voted_at": "2026-07-23T17:33:59.611Z"
+ },
+ {
+ "id": "b14c4e0ccc4e",
+ "title": "All-Models — Weather Card",
+ "prompt": "Build a single-file HTML page: an animated weather card UI. Show a city, temperature, and condition with a matching CSS/canvas animation (sun rays, drifting clouds, rain, or snow); a button cycles through 4 weather states. Glassmorphism aesthetic, self-contained, no external libraries or APIs.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T17:34:24.809Z",
+ "winner": "kimi",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 34,
+ "cost": 0,
+ "started_at": "2026-07-23T17:34:24.839Z",
+ "finished_at": "2026-07-23T17:34:58.613Z",
+ "queued_at": "2026-07-23T17:34:24.814Z",
+ "bytes": 4177,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The weather card is visually appealing and fulfills the challenge requirements but lacks some polish in terms of animation smoothness.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 32,
+ "cost": 0,
+ "started_at": "2026-07-23T17:34:58.617Z",
+ "finished_at": "2026-07-23T17:35:30.425Z",
+ "queued_at": "2026-07-23T17:34:24.816Z",
+ "bytes": 4322,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The weather card is functional and visually appealing but lacks some polish in the animation and button design.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 13,
+ "cost": 0,
+ "started_at": "2026-07-23T17:35:30.430Z",
+ "finished_at": "2026-07-23T17:35:43.625Z",
+ "queued_at": "2026-07-23T17:34:24.819Z",
+ "bytes": 3374,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The weather card is functional and visually appealing but lacks the CSS/canvas animations required by the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 32,
+ "cost": 0,
+ "started_at": "2026-07-23T17:34:24.840Z",
+ "finished_at": "2026-07-23T17:34:56.600Z",
+ "queued_at": "2026-07-23T17:34:24.823Z",
+ "bytes": 5472,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The weather card is functional and visually appealing but lacks the canvas animation specified in the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 67,
+ "cost": 0,
+ "started_at": "2026-07-23T17:35:43.630Z",
+ "finished_at": "2026-07-23T17:36:50.768Z",
+ "queued_at": "2026-07-23T17:34:24.826Z",
+ "bytes": 4437,
+ "thumb": true,
+ "aiScore": 8,
+ "aiReason": "The weather card is visually appealing and fulfills all the requirements of the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7
+ },
+ "aiSpread": 2
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 87,
+ "cost": 0,
+ "started_at": "2026-07-23T17:34:24.841Z",
+ "finished_at": "2026-07-23T17:35:52.052Z",
+ "queued_at": "2026-07-23T17:34:24.829Z",
+ "bytes": 14512,
+ "thumb": true,
+ "aiScore": 9,
+ "aiReason": "The weather card is visually appealing and fulfills all the requirements of the challenge with a smooth animation and glassmorphism aesthetic.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 128,
+ "cost": 0.0169,
+ "started_at": "2026-07-23T17:34:24.845Z",
+ "finished_at": "2026-07-23T17:36:33.049Z",
+ "queued_at": "2026-07-23T17:34:24.831Z",
+ "bytes": 12493,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The weather card fulfills the challenge with an animated sun and a glassmorphism aesthetic. The button functionality is also included.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 72,
+ "cost": 0.1124,
+ "started_at": "2026-07-23T17:34:24.846Z",
+ "finished_at": "2026-07-23T17:35:37.267Z",
+ "queued_at": "2026-07-23T17:34:24.834Z",
+ "bytes": 25752,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The weather card is well-designed and fulfills the challenge criteria effectively. The CSS animation adds a dynamic touch to the UI.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 58,
+ "cost": 0.0747,
+ "started_at": "2026-07-23T17:34:24.848Z",
+ "finished_at": "2026-07-23T17:35:22.489Z",
+ "queued_at": "2026-07-23T17:34:24.836Z",
+ "bytes": 12912,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The weather card is visually appealing and fulfills the challenge requirements effectively.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T17:37:34.349Z",
+ "aiPick": "kimi",
+ "voted_at": "2026-07-23T17:37:35.420Z"
+ },
+ {
+ "id": "52fc117697fd",
+ "title": "All-Models — Fireworks Finale",
+ "prompt": "Build a single-file HTML page: an interactive fireworks display. Click to launch shells that explode into colorful particle bursts with gravity and fade; auto-launch toggle; dark sky with subtle trails. Canvas-based, self-contained, no external libraries.",
+ "category": "Games",
+ "designTools": false,
+ "created_at": "2026-07-23T17:37:49.974Z",
+ "winner": "gpt",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 27,
+ "cost": 0,
+ "started_at": "2026-07-23T17:37:50.006Z",
+ "finished_at": "2026-07-23T17:38:17.126Z",
+ "queued_at": "2026-07-23T17:37:49.979Z",
+ "bytes": 4066,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page has basic functionality but lacks visual appeal and detail.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 36,
+ "cost": 0,
+ "started_at": "2026-07-23T17:38:17.129Z",
+ "finished_at": "2026-07-23T17:38:53.396Z",
+ "queued_at": "2026-07-23T17:37:49.983Z",
+ "bytes": 3717,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is interactive and fulfills the challenge requirements but lacks visual detail in the particle effects.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 19,
+ "cost": 0,
+ "started_at": "2026-07-23T17:38:53.399Z",
+ "finished_at": "2026-07-23T17:39:12.781Z",
+ "queued_at": "2026-07-23T17:37:49.987Z",
+ "bytes": 2987,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is interactive and fulfills the challenge requirements but lacks visual quality due to a lack of color and detail.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 13,
+ "cost": 0,
+ "started_at": "2026-07-23T17:37:50.008Z",
+ "finished_at": "2026-07-23T17:38:02.694Z",
+ "queued_at": "2026-07-23T17:37:49.990Z",
+ "bytes": 1917,
+ "thumb": true,
+ "aiScore": 6.3,
+ "aiReason": "The page is interactive and fulfills the challenge requirements but lacks visual quality due to poor rendering of particle effects.",
+ "aiScores": {
+ "qwen2.5vl:7b": 6,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 35,
+ "cost": 0,
+ "started_at": "2026-07-23T17:39:12.786Z",
+ "finished_at": "2026-07-23T17:39:47.840Z",
+ "queued_at": "2026-07-23T17:37:49.993Z",
+ "bytes": 2496,
+ "thumb": true,
+ "aiScore": 5.8,
+ "aiReason": "The page is interactive and fulfills the challenge requirements but lacks visual quality due to a near-black background.",
+ "aiScores": {
+ "qwen2.5vl:7b": 5,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 1.5
+ },
+ {
+ "model": "claude-code",
+ "status": "error",
+ "error": "no HTML document in model output (96 chars)",
+ "seconds": 3,
+ "cost": null,
+ "started_at": "2026-07-23T17:37:50.010Z",
+ "finished_at": "2026-07-23T17:37:52.803Z",
+ "queued_at": "2026-07-23T17:37:49.996Z"
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 76,
+ "cost": 0.0117,
+ "started_at": "2026-07-23T17:37:50.015Z",
+ "finished_at": "2026-07-23T17:39:06.342Z",
+ "queued_at": "2026-07-23T17:37:49.999Z",
+ "bytes": 7859,
+ "thumb": true,
+ "aiScore": 3.8,
+ "aiReason": "The page is blank and does not display any fireworks or interactive elements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 3,
+ "minicpm-v:latest": 4.5
+ },
+ "aiSpread": 1.5
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 25,
+ "cost": 0.0387,
+ "started_at": "2026-07-23T17:37:50.016Z",
+ "finished_at": "2026-07-23T17:38:14.709Z",
+ "queued_at": "2026-07-23T17:37:50.001Z",
+ "bytes": 8697,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The fireworks display is visually appealing and fulfills all the requirements of the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 29,
+ "cost": 0.0406,
+ "started_at": "2026-07-23T17:37:50.019Z",
+ "finished_at": "2026-07-23T17:38:19.150Z",
+ "queued_at": "2026-07-23T17:37:50.003Z",
+ "bytes": 6893,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The fireworks display is interactive and fulfills the challenge requirements. However, the visual quality could be improved by adding more colors or patterns.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T17:40:50.417Z",
+ "aiPick": "gpt",
+ "voted_at": "2026-07-23T17:40:50.660Z"
+ },
+ {
+ "id": "a3958f97e739",
+ "title": "All-Models — Piano Keys",
+ "prompt": "Build a single-file HTML page: a playable one-octave piano. Click or press keyboard rows to play notes via WebAudio oscillators with an ADSR envelope; keys light up; a waveform selector (sine/square/saw) and a sustain toggle. Self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T17:41:10.779Z",
+ "winner": "claude-code",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 38,
+ "cost": 0,
+ "started_at": "2026-07-23T17:41:10.812Z",
+ "finished_at": "2026-07-23T17:41:48.692Z",
+ "queued_at": "2026-07-23T17:41:10.785Z",
+ "bytes": 5604,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is functional and meets the challenge criteria but lacks visual appeal.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 40,
+ "cost": 0,
+ "started_at": "2026-07-23T17:41:48.696Z",
+ "finished_at": "2026-07-23T17:42:28.737Z",
+ "queued_at": "2026-07-23T17:41:10.790Z",
+ "bytes": 6183,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is functional and meets the challenge criteria but lacks visual polish.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 24,
+ "cost": 0,
+ "started_at": "2026-07-23T17:42:28.742Z",
+ "finished_at": "2026-07-23T17:42:52.416Z",
+ "queued_at": "2026-07-23T17:41:10.793Z",
+ "bytes": 3907,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The piano is functional and visually clear but lacks some polish in terms of design.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 21,
+ "cost": 0,
+ "started_at": "2026-07-23T17:41:10.814Z",
+ "finished_at": "2026-07-23T17:41:31.596Z",
+ "queued_at": "2026-07-23T17:41:10.796Z",
+ "bytes": 3039,
+ "thumb": true,
+ "aiScore": 5.5,
+ "aiReason": "The page is empty and does not display any functionality or visual elements related to the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 4,
+ "minicpm-v:latest": 7
+ },
+ "aiSpread": 3
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 64,
+ "cost": 0,
+ "started_at": "2026-07-23T17:42:52.421Z",
+ "finished_at": "2026-07-23T17:43:56.339Z",
+ "queued_at": "2026-07-23T17:41:10.799Z",
+ "bytes": 3951,
+ "thumb": true,
+ "aiScore": 6.5,
+ "aiReason": "The page is functional but lacks visual appeal and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6
+ },
+ "aiSpread": 1
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 57,
+ "cost": 0,
+ "started_at": "2026-07-23T17:41:10.815Z",
+ "finished_at": "2026-07-23T17:42:07.696Z",
+ "queued_at": "2026-07-23T17:41:10.802Z",
+ "bytes": 8672,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The piano is fully functional and visually appealing, meeting all the challenge criteria.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 94,
+ "cost": 0.0157,
+ "started_at": "2026-07-23T17:41:10.820Z",
+ "finished_at": "2026-07-23T17:42:44.403Z",
+ "queued_at": "2026-07-23T17:41:10.805Z",
+ "bytes": 7770,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The page is functional and meets the challenge criteria but lacks some polish in visual design.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 26,
+ "cost": 0.0557,
+ "started_at": "2026-07-23T17:41:10.822Z",
+ "finished_at": "2026-07-23T17:41:36.859Z",
+ "queued_at": "2026-07-23T17:41:10.807Z",
+ "bytes": 13471,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The piano is fully functional and visually appealing, meeting all the criteria of the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 49,
+ "cost": 0.0598,
+ "started_at": "2026-07-23T17:41:10.823Z",
+ "finished_at": "2026-07-23T17:41:59.630Z",
+ "queued_at": "2026-07-23T17:41:10.810Z",
+ "bytes": 11532,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The piano is functional and visually appealing, fulfilling all the requirements of the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T17:44:41.109Z",
+ "aiPick": "claude-code",
+ "voted_at": "2026-07-23T17:44:41.545Z"
+ },
+ {
+ "id": "e85044630905",
+ "title": "All-Models — Rubik Cube 3D",
+ "prompt": "Build a single-file HTML page: a 3D Rubiks cube you can rotate. Drag to orbit the camera; buttons rotate faces with animation; a scramble and a reset button. Pure CSS 3D transforms or canvas math, self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T17:45:01.960Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 85,
+ "cost": 0,
+ "started_at": "2026-07-23T17:45:01.992Z",
+ "finished_at": "2026-07-23T17:46:27.379Z",
+ "queued_at": "2026-07-23T17:45:01.964Z",
+ "bytes": 10888,
+ "thumb": true
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 54,
+ "cost": 0,
+ "started_at": "2026-07-23T17:46:27.382Z",
+ "finished_at": "2026-07-23T17:47:21.375Z",
+ "queued_at": "2026-07-23T17:45:01.966Z",
+ "bytes": 5105,
+ "thumb": true
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 23,
+ "cost": 0,
+ "started_at": "2026-07-23T17:47:21.383Z",
+ "finished_at": "2026-07-23T17:47:44.228Z",
+ "queued_at": "2026-07-23T17:45:01.970Z",
+ "bytes": 4395,
+ "thumb": true
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 23,
+ "cost": 0,
+ "started_at": "2026-07-23T17:45:01.995Z",
+ "finished_at": "2026-07-23T17:45:24.558Z",
+ "queued_at": "2026-07-23T17:45:01.973Z",
+ "bytes": 3947,
+ "thumb": true
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 54,
+ "cost": 0,
+ "started_at": "2026-07-23T17:47:44.231Z",
+ "finished_at": "2026-07-23T17:48:38.383Z",
+ "queued_at": "2026-07-23T17:45:01.977Z",
+ "bytes": 3902,
+ "thumb": true
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 203,
+ "cost": 0,
+ "started_at": "2026-07-23T17:45:02.001Z",
+ "finished_at": "2026-07-23T17:48:24.826Z",
+ "queued_at": "2026-07-23T17:45:01.981Z",
+ "bytes": 9584,
+ "thumb": true
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 258,
+ "cost": 0.0376,
+ "started_at": "2026-07-23T17:45:02.005Z",
+ "finished_at": "2026-07-23T17:49:19.702Z",
+ "queued_at": "2026-07-23T17:45:01.984Z",
+ "bytes": 8466,
+ "thumb": true
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 42,
+ "cost": 0.0436,
+ "started_at": "2026-07-23T17:45:02.008Z",
+ "finished_at": "2026-07-23T17:45:44.411Z",
+ "queued_at": "2026-07-23T17:45:01.987Z",
+ "bytes": 10284,
+ "thumb": true
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 47,
+ "cost": 0.0604,
+ "started_at": "2026-07-23T17:45:02.011Z",
+ "finished_at": "2026-07-23T17:45:49.028Z",
+ "queued_at": "2026-07-23T17:45:01.990Z",
+ "bytes": 11795,
+ "thumb": true
+ }
+ ],
+ "judging": true,
+ "judged_at": null
+ },
+ {
+ "id": "f83743a0fa7e",
+ "title": "Rentv.com suggested upgrades",
+ "prompt": "Rentv.com suggested upgrades",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T17:48:52.443Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 22,
+ "cost": 0,
+ "started_at": "2026-07-23T17:48:52.482Z",
+ "finished_at": "2026-07-23T17:49:14.223Z",
+ "queued_at": "2026-07-23T17:48:52.449Z",
+ "bytes": 2826,
+ "thumb": true
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 21,
+ "cost": 0,
+ "started_at": "2026-07-23T17:49:14.227Z",
+ "finished_at": "2026-07-23T17:49:34.796Z",
+ "queued_at": "2026-07-23T17:48:52.454Z",
+ "bytes": 2980,
+ "thumb": true
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T17:49:34.799Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T17:48:52.458Z"
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 7,
+ "cost": 0,
+ "started_at": "2026-07-23T17:48:52.485Z",
+ "finished_at": "2026-07-23T17:48:59.275Z",
+ "queued_at": "2026-07-23T17:48:52.461Z",
+ "bytes": 1089,
+ "thumb": true
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "queued",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": null,
+ "finished_at": null,
+ "queued_at": "2026-07-23T17:48:52.465Z"
+ },
+ {
+ "model": "claude-code",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T17:48:52.487Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T17:48:52.469Z"
+ },
+ {
+ "model": "kimi",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T17:49:19.707Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T17:48:52.473Z"
+ },
+ {
+ "model": "gpt",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T17:48:52.495Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T17:48:52.476Z"
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 82,
+ "cost": 0.1014,
+ "started_at": "2026-07-23T17:48:52.498Z",
+ "finished_at": "2026-07-23T17:50:14.119Z",
+ "queued_at": "2026-07-23T17:48:52.479Z",
+ "bytes": 20119,
"thumb": true
}
]
diff --git a/data/costlog.jsonl b/data/costlog.jsonl
index de73a4e..9f0a9b0 100644
--- a/data/costlog.jsonl
+++ b/data/costlog.jsonl
@@ -99,3 +99,23 @@
{"ts":"2026-07-23T17:14:56.473Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":142,"output_tokens":9228,"cost_usd":0.023155}
{"ts":"2026-07-23T17:18:22.577Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":144,"output_tokens":2417,"cost_usd":0.03409}
{"ts":"2026-07-23T17:19:05.290Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":347,"output_tokens":5540,"cost_usd":0.084141}
+{"ts":"2026-07-23T17:20:46.915Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":145,"output_tokens":8261,"cost_usd":0.02074}
+{"ts":"2026-07-23T17:23:03.014Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":341,"output_tokens":6446,"cost_usd":0.097713}
+{"ts":"2026-07-23T17:23:27.472Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":134,"output_tokens":10591,"cost_usd":0.148508}
+{"ts":"2026-07-23T17:24:38.696Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":134,"output_tokens":7940,"cost_usd":0.01993}
+{"ts":"2026-07-23T17:30:08.032Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":349,"output_tokens":6243,"cost_usd":0.094692}
+{"ts":"2026-07-23T17:30:14.108Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":142,"output_tokens":7000,"cost_usd":0.098249}
+{"ts":"2026-07-23T17:33:12.806Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":144,"output_tokens":13032,"cost_usd":0.032666}
+{"ts":"2026-07-23T17:35:22.486Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":351,"output_tokens":4910,"cost_usd":0.074703}
+{"ts":"2026-07-23T17:35:37.263Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":144,"output_tokens":8013,"cost_usd":0.112434}
+{"ts":"2026-07-23T17:36:33.045Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":145,"output_tokens":6725,"cost_usd":0.0169}
+{"ts":"2026-07-23T17:38:14.705Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":128,"output_tokens":2746,"cost_usd":0.038668}
+{"ts":"2026-07-23T17:38:19.149Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":336,"output_tokens":2641,"cost_usd":0.040623}
+{"ts":"2026-07-23T17:39:06.339Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":130,"output_tokens":4665,"cost_usd":0.01174}
+{"ts":"2026-07-23T17:41:36.855Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":142,"output_tokens":3961,"cost_usd":0.055703}
+{"ts":"2026-07-23T17:41:59.627Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":346,"output_tokens":3920,"cost_usd":0.059838}
+{"ts":"2026-07-23T17:42:44.400Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":143,"output_tokens":6234,"cost_usd":0.015671}
+{"ts":"2026-07-23T17:45:44.409Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":135,"output_tokens":3095,"cost_usd":0.043566}
+{"ts":"2026-07-23T17:45:49.027Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":341,"output_tokens":3961,"cost_usd":0.060438}
+{"ts":"2026-07-23T17:49:19.698Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":136,"output_tokens":15000,"cost_usd":0.037582}
+{"ts":"2026-07-23T17:50:14.116Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":291,"output_tokens":6702,"cost_usd":0.101403}
diff --git a/data/votes.jsonl b/data/votes.jsonl
index 2d1573d..191d9f7 100644
--- a/data/votes.jsonl
+++ b/data/votes.jsonl
@@ -22,3 +22,9 @@
{"ts":"2026-07-23T17:08:46.439Z","challenge":"0e969e848172","title":"All-Models — Gravity Sandbox","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code","kimi","gpt","grok"]}
{"ts":"2026-07-23T17:11:39.378Z","challenge":"e3c185541c0d","title":"All-Models — Matrix Rain","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code","kimi","gpt","grok"]}
{"ts":"2026-07-23T17:17:41.520Z","challenge":"905cc3052af2","title":"All-Models — Physics Domino Run","winner":"grok","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","kimi","gpt","grok"]}
+{"ts":"2026-07-23T17:21:36.681Z","challenge":"48026228790d","title":"All-Models — Voronoi Shatter","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code","kimi","gpt","grok"]}
+{"ts":"2026-07-23T17:28:42.553Z","challenge":"673cf6b31c78","title":"All-Models — Snake Reimagined","winner":"gpt","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code","kimi","gpt","grok"]}
+{"ts":"2026-07-23T17:33:59.611Z","challenge":"9ba7a5497a50","title":"All-Models — Clock Gallery","winner":"gpt","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code","kimi","gpt","grok"]}
+{"ts":"2026-07-23T17:37:35.420Z","challenge":"b14c4e0ccc4e","title":"All-Models — Weather Card","winner":"kimi","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code","kimi","gpt","grok"]}
+{"ts":"2026-07-23T17:40:50.660Z","challenge":"52fc117697fd","title":"All-Models — Fireworks Finale","winner":"gpt","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","kimi","gpt","grok"]}
+{"ts":"2026-07-23T17:44:41.545Z","challenge":"a3958f97e739","title":"All-Models — Piano Keys","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code","kimi","gpt","grok"]}
← 543fc51 auto-save: 2026-07-23T10:20:16 (23 files) — data/challenges.
·
back to Model Arena
·
arena: iTerm2 Build button on every model card + publish *.a af34518 →