← back to Model Arena
auto-save: 2026-07-23T12:20:57 (15 files) — data/challenges.json data/votes.jsonl data/artifacts/43dfd86f6460/ data/artifacts/48026228790d/gemma3-12b.png data/artifacts/5dbb1c6732ae/
34b99c1cf6d5e577ab2c4a302f0df9b392735865 · 2026-07-23 12:21:07 -0700 · Steve Abrams
Files touched
A data/artifacts/43dfd86f6460/claude-code.htmlA data/artifacts/43dfd86f6460/claude-code.pngA data/artifacts/43dfd86f6460/gemma3-12b.htmlA data/artifacts/43dfd86f6460/gemma3-12b.pngA data/artifacts/43dfd86f6460/hermes3-8b.htmlA data/artifacts/43dfd86f6460/hermes3-8b.pngA data/artifacts/43dfd86f6460/hf-qwen-coder-32b.htmlA data/artifacts/43dfd86f6460/hf-qwen-coder-32b.pngA data/artifacts/43dfd86f6460/qwen25-7b.htmlA data/artifacts/43dfd86f6460/qwen25-7b.pngA data/artifacts/43dfd86f6460/qwen3-14b.htmlA data/artifacts/43dfd86f6460/qwen3-14b.pngA data/artifacts/48026228790d/gemma3-12b.pngA data/artifacts/5dbb1c6732ae/claude-code.htmlA data/artifacts/5dbb1c6732ae/claude-code.pngA data/artifacts/5dbb1c6732ae/gemma3-12b.htmlA data/artifacts/5dbb1c6732ae/gemma3-12b.pngA data/artifacts/5dbb1c6732ae/hermes3-8b.htmlA data/artifacts/5dbb1c6732ae/hermes3-8b.pngA data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.htmlA data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.pngA data/artifacts/5dbb1c6732ae/qwen25-7b.htmlA data/artifacts/5dbb1c6732ae/qwen25-7b.pngA data/artifacts/5dbb1c6732ae/qwen3-14b.htmlA data/artifacts/5dbb1c6732ae/qwen3-14b.pngA data/artifacts/68b40458a368/hermes3-8b.htmlA data/artifacts/68b40458a368/hf-qwen-coder-32b.htmlA data/artifacts/68b40458a368/hf-qwen-coder-32b.pngA data/artifacts/78dd0efa6a47/claude-code.htmlA data/artifacts/78dd0efa6a47/claude-code.pngA data/artifacts/78dd0efa6a47/gemma3-12b.htmlA data/artifacts/78dd0efa6a47/gemma3-12b.pngA data/artifacts/78dd0efa6a47/hermes3-8b.htmlA data/artifacts/78dd0efa6a47/hermes3-8b.pngA data/artifacts/78dd0efa6a47/qwen25-7b.htmlA data/artifacts/78dd0efa6a47/qwen25-7b.pngA data/artifacts/78dd0efa6a47/qwen3-14b.htmlA data/artifacts/78dd0efa6a47/qwen3-14b.pngA data/artifacts/7993245ce013/claude-code.htmlA data/artifacts/7993245ce013/claude-code.pngA data/artifacts/7993245ce013/gemma3-12b.htmlA data/artifacts/7993245ce013/gemma3-12b.pngA data/artifacts/7993245ce013/hermes3-8b.htmlA data/artifacts/7993245ce013/hermes3-8b.pngA data/artifacts/7993245ce013/hf-qwen-coder-32b.htmlA data/artifacts/7993245ce013/hf-qwen-coder-32b.pngA data/artifacts/7993245ce013/qwen25-7b.htmlA data/artifacts/7993245ce013/qwen25-7b.pngA data/artifacts/7993245ce013/qwen3-14b.htmlA data/artifacts/7993245ce013/qwen3-14b.pngA data/artifacts/a03b18e65a8c/claude-code.htmlA data/artifacts/a03b18e65a8c/claude-code.pngA data/artifacts/a03b18e65a8c/gemma3-12b.htmlA data/artifacts/a03b18e65a8c/gemma3-12b.pngA data/artifacts/a03b18e65a8c/hermes3-8b.htmlA data/artifacts/a03b18e65a8c/hermes3-8b.pngA data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.htmlA data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.pngA data/artifacts/a03b18e65a8c/qwen25-7b.htmlA data/artifacts/a03b18e65a8c/qwen25-7b.pngA data/artifacts/a03b18e65a8c/qwen3-14b.htmlA data/artifacts/a03b18e65a8c/qwen3-14b.pngA data/artifacts/a7640ab6a2fa/claude-code.htmlA data/artifacts/a7640ab6a2fa/claude-code.pngA data/artifacts/a7640ab6a2fa/gemma3-12b.htmlA data/artifacts/a7640ab6a2fa/gemma3-12b.pngA data/artifacts/a7640ab6a2fa/hermes3-8b.htmlA data/artifacts/a7640ab6a2fa/hermes3-8b.pngA data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.htmlA data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.pngA data/artifacts/a7640ab6a2fa/qwen25-7b.htmlA data/artifacts/a7640ab6a2fa/qwen25-7b.pngA data/artifacts/a7640ab6a2fa/qwen3-14b.htmlA data/artifacts/a7640ab6a2fa/qwen3-14b.pngA data/artifacts/f0251ce85742/claude-code.htmlA data/artifacts/f0251ce85742/claude-code.pngA data/artifacts/f0251ce85742/gemma3-12b.htmlA data/artifacts/f0251ce85742/gemma3-12b.pngA data/artifacts/f0251ce85742/hermes3-8b.htmlA data/artifacts/f0251ce85742/hermes3-8b.pngA data/artifacts/f0251ce85742/hf-qwen-coder-32b.htmlA data/artifacts/f0251ce85742/hf-qwen-coder-32b.pngA data/artifacts/f0251ce85742/qwen25-7b.htmlA data/artifacts/f0251ce85742/qwen25-7b.pngA data/artifacts/f0251ce85742/qwen3-14b.htmlA data/artifacts/f0251ce85742/qwen3-14b.pngM data/challenges.jsonA data/landings/a7640ab6a2fa-claude-code.htmlM data/votes.jsonlA star-clock.html
Diff
commit 34b99c1cf6d5e577ab2c4a302f0df9b392735865
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 23 12:21:07 2026 -0700
auto-save: 2026-07-23T12:20:57 (15 files) — data/challenges.json data/votes.jsonl data/artifacts/43dfd86f6460/ data/artifacts/48026228790d/gemma3-12b.png data/artifacts/5dbb1c6732ae/
---
data/artifacts/43dfd86f6460/claude-code.html | 319 +++++++
data/artifacts/43dfd86f6460/claude-code.png | Bin 0 -> 183727 bytes
data/artifacts/43dfd86f6460/gemma3-12b.html | 163 ++++
data/artifacts/43dfd86f6460/gemma3-12b.png | Bin 0 -> 5760 bytes
data/artifacts/43dfd86f6460/hermes3-8b.html | 112 +++
data/artifacts/43dfd86f6460/hermes3-8b.png | Bin 0 -> 2844 bytes
data/artifacts/43dfd86f6460/hf-qwen-coder-32b.html | 96 ++
data/artifacts/43dfd86f6460/hf-qwen-coder-32b.png | Bin 0 -> 10072 bytes
data/artifacts/43dfd86f6460/qwen25-7b.html | 81 ++
data/artifacts/43dfd86f6460/qwen25-7b.png | Bin 0 -> 2832 bytes
data/artifacts/43dfd86f6460/qwen3-14b.html | 157 ++++
data/artifacts/43dfd86f6460/qwen3-14b.png | Bin 0 -> 7736 bytes
data/artifacts/48026228790d/gemma3-12b.png | Bin 0 -> 232649 bytes
data/artifacts/5dbb1c6732ae/claude-code.html | 363 ++++++++
data/artifacts/5dbb1c6732ae/claude-code.png | Bin 0 -> 223308 bytes
data/artifacts/5dbb1c6732ae/gemma3-12b.html | 149 ++++
data/artifacts/5dbb1c6732ae/gemma3-12b.png | Bin 0 -> 31584 bytes
data/artifacts/5dbb1c6732ae/hermes3-8b.html | 85 ++
data/artifacts/5dbb1c6732ae/hermes3-8b.png | Bin 0 -> 16350 bytes
data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.html | 105 +++
data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.png | Bin 0 -> 12762 bytes
data/artifacts/5dbb1c6732ae/qwen25-7b.html | 60 ++
data/artifacts/5dbb1c6732ae/qwen25-7b.png | Bin 0 -> 4832 bytes
data/artifacts/5dbb1c6732ae/qwen3-14b.html | 175 ++++
data/artifacts/5dbb1c6732ae/qwen3-14b.png | Bin 0 -> 4708 bytes
data/artifacts/68b40458a368/hermes3-8b.html | 166 ++++
data/artifacts/68b40458a368/hf-qwen-coder-32b.html | 164 ++++
data/artifacts/68b40458a368/hf-qwen-coder-32b.png | Bin 0 -> 4277 bytes
data/artifacts/78dd0efa6a47/claude-code.html | 381 ++++++++
data/artifacts/78dd0efa6a47/claude-code.png | Bin 0 -> 224927 bytes
data/artifacts/78dd0efa6a47/gemma3-12b.html | 156 ++++
data/artifacts/78dd0efa6a47/gemma3-12b.png | Bin 0 -> 14804 bytes
data/artifacts/78dd0efa6a47/hermes3-8b.html | 137 +++
data/artifacts/78dd0efa6a47/hermes3-8b.png | Bin 0 -> 9808 bytes
data/artifacts/78dd0efa6a47/qwen25-7b.html | 94 ++
data/artifacts/78dd0efa6a47/qwen25-7b.png | Bin 0 -> 14041 bytes
data/artifacts/78dd0efa6a47/qwen3-14b.html | 162 ++++
data/artifacts/78dd0efa6a47/qwen3-14b.png | Bin 0 -> 7583 bytes
data/artifacts/7993245ce013/claude-code.html | 250 ++++++
data/artifacts/7993245ce013/claude-code.png | Bin 0 -> 152985 bytes
data/artifacts/7993245ce013/gemma3-12b.html | 206 +++++
data/artifacts/7993245ce013/gemma3-12b.png | Bin 0 -> 9142 bytes
data/artifacts/7993245ce013/hermes3-8b.html | 239 +++++
data/artifacts/7993245ce013/hermes3-8b.png | Bin 0 -> 10853 bytes
data/artifacts/7993245ce013/hf-qwen-coder-32b.html | 129 +++
data/artifacts/7993245ce013/hf-qwen-coder-32b.png | Bin 0 -> 13159 bytes
data/artifacts/7993245ce013/qwen25-7b.html | 105 +++
data/artifacts/7993245ce013/qwen25-7b.png | Bin 0 -> 15013 bytes
data/artifacts/7993245ce013/qwen3-14b.html | 215 +++++
data/artifacts/7993245ce013/qwen3-14b.png | Bin 0 -> 12507 bytes
data/artifacts/a03b18e65a8c/claude-code.html | 312 +++++++
data/artifacts/a03b18e65a8c/claude-code.png | Bin 0 -> 23310 bytes
data/artifacts/a03b18e65a8c/gemma3-12b.html | 140 +++
data/artifacts/a03b18e65a8c/gemma3-12b.png | Bin 0 -> 8629 bytes
data/artifacts/a03b18e65a8c/hermes3-8b.html | 112 +++
data/artifacts/a03b18e65a8c/hermes3-8b.png | Bin 0 -> 4125 bytes
data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.html | 138 +++
data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.png | Bin 0 -> 7289 bytes
data/artifacts/a03b18e65a8c/qwen25-7b.html | 129 +++
data/artifacts/a03b18e65a8c/qwen25-7b.png | Bin 0 -> 5340 bytes
data/artifacts/a03b18e65a8c/qwen3-14b.html | 173 ++++
data/artifacts/a03b18e65a8c/qwen3-14b.png | Bin 0 -> 9661 bytes
data/artifacts/a7640ab6a2fa/claude-code.html | 333 +++++++
data/artifacts/a7640ab6a2fa/claude-code.png | Bin 0 -> 110546 bytes
data/artifacts/a7640ab6a2fa/gemma3-12b.html | 103 +++
data/artifacts/a7640ab6a2fa/gemma3-12b.png | Bin 0 -> 12329 bytes
data/artifacts/a7640ab6a2fa/hermes3-8b.html | 62 ++
data/artifacts/a7640ab6a2fa/hermes3-8b.png | Bin 0 -> 209639 bytes
data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.html | 85 ++
data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.png | Bin 0 -> 6973 bytes
data/artifacts/a7640ab6a2fa/qwen25-7b.html | 66 ++
data/artifacts/a7640ab6a2fa/qwen25-7b.png | Bin 0 -> 11875 bytes
data/artifacts/a7640ab6a2fa/qwen3-14b.html | 155 ++++
data/artifacts/a7640ab6a2fa/qwen3-14b.png | Bin 0 -> 17693 bytes
data/artifacts/f0251ce85742/claude-code.html | 329 +++++++
data/artifacts/f0251ce85742/claude-code.png | Bin 0 -> 17657 bytes
data/artifacts/f0251ce85742/gemma3-12b.html | 204 +++++
data/artifacts/f0251ce85742/gemma3-12b.png | Bin 0 -> 2724 bytes
data/artifacts/f0251ce85742/hermes3-8b.html | 185 ++++
data/artifacts/f0251ce85742/hermes3-8b.png | Bin 0 -> 2825 bytes
data/artifacts/f0251ce85742/hf-qwen-coder-32b.html | 108 +++
data/artifacts/f0251ce85742/hf-qwen-coder-32b.png | Bin 0 -> 4536 bytes
data/artifacts/f0251ce85742/qwen25-7b.html | 103 +++
data/artifacts/f0251ce85742/qwen25-7b.png | Bin 0 -> 5692 bytes
data/artifacts/f0251ce85742/qwen3-14b.html | 243 +++++
data/artifacts/f0251ce85742/qwen3-14b.png | Bin 0 -> 4476 bytes
data/challenges.json | 989 ++++++++++++++++++++-
data/landings/a7640ab6a2fa-claude-code.html | 342 +++++++
data/votes.jsonl | 7 +
star-clock.html | 364 ++++++++
90 files changed, 8934 insertions(+), 17 deletions(-)
diff --git a/data/artifacts/43dfd86f6460/claude-code.html b/data/artifacts/43dfd86f6460/claude-code.html
new file mode 100644
index 0000000..1c9dd1f
--- /dev/null
+++ b/data/artifacts/43dfd86f6460/claude-code.html
@@ -0,0 +1,319 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+<title>XY Synth Pad</title>
+<style>
+ :root{
+ --bg:#0a0a12;
+ --panel:#12121f;
+ --accent:#00e5ff;
+ --accent2:#ff3d81;
+ --text:#e8e8f0;
+ --muted:#8a8aa0;
+ }
+ *{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent;}
+ html,body{height:100%;}
+ body{
+ background:radial-gradient(circle at 30% 20%,#1a1a2e,#0a0a12 70%);
+ color:var(--text);
+ font-family:'Segoe UI',system-ui,-apple-system,sans-serif;
+ display:flex;flex-direction:column;align-items:center;
+ min-height:100%;padding:16px;gap:14px;overflow-x:hidden;
+ }
+ h1{
+ font-size:15px;font-weight:600;letter-spacing:3px;text-transform:uppercase;
+ color:var(--muted);display:flex;align-items:center;gap:10px;
+ }
+ h1 .dot{width:8px;height:8px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent);}
+
+ .wrap{width:100%;max-width:560px;display:flex;flex-direction:column;gap:14px;}
+
+ /* Note display */
+ .note-display{
+ background:var(--panel);border:1px solid #22223a;border-radius:14px;
+ padding:14px 18px;display:flex;align-items:center;justify-content:space-between;
+ gap:14px;position:relative;overflow:hidden;
+ }
+ .note-display .glow{
+ position:absolute;inset:0;opacity:0;transition:opacity .15s;
+ background:radial-gradient(circle at 20% 50%,rgba(0,229,255,.18),transparent 60%);
+ pointer-events:none;
+ }
+ .note-display.active .glow{opacity:1;}
+ .note-main{display:flex;align-items:baseline;gap:8px;}
+ .note-name{font-size:44px;font-weight:700;line-height:1;font-variant-numeric:tabular-nums;
+ background:linear-gradient(120deg,var(--accent),var(--accent2));
+ -webkit-background-clip:text;background-clip:text;color:transparent;min-width:96px;}
+ .note-oct{font-size:20px;color:var(--muted);font-weight:600;}
+ .readouts{display:flex;flex-direction:column;gap:4px;text-align:right;font-variant-numeric:tabular-nums;}
+ .readouts .row{font-size:12px;color:var(--muted);}
+ .readouts .row b{color:var(--text);font-weight:600;}
+
+ /* Pad */
+ .pad{
+ position:relative;width:100%;aspect-ratio:1/0.8;border-radius:16px;
+ background:
+ linear-gradient(0deg,rgba(255,61,129,.06),transparent),
+ linear-gradient(90deg,rgba(0,229,255,.06),transparent),
+ #0d0d18;
+ border:1px solid #24243c;overflow:hidden;cursor:crosshair;touch-action:none;
+ box-shadow:inset 0 0 60px rgba(0,0,0,.6);
+ }
+ .pad .grid{position:absolute;inset:0;
+ background-image:
+ linear-gradient(rgba(255,255,255,.04) 1px,transparent 1px),
+ linear-gradient(90deg,rgba(255,255,255,.04) 1px,transparent 1px);
+ background-size:9.09% 8.33%;pointer-events:none;}
+ .pad .axis-label{position:absolute;font-size:10px;letter-spacing:2px;color:var(--muted);
+ text-transform:uppercase;pointer-events:none;}
+ .pad .ax-x{bottom:8px;left:50%;transform:translateX(-50%);}
+ .pad .ax-y{top:50%;left:8px;transform:translateY(-50%) rotate(-90deg);transform-origin:left;}
+ .pad .hint{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
+ color:var(--muted);font-size:14px;letter-spacing:1px;pointer-events:none;transition:opacity .3s;}
+ .pad.playing .hint{opacity:0;}
+
+ .cursor{position:absolute;width:26px;height:26px;border-radius:50%;
+ border:2px solid var(--accent);transform:translate(-50%,-50%) scale(0);
+ box-shadow:0 0 18px var(--accent),inset 0 0 10px rgba(0,229,255,.4);
+ transition:transform .12s ease-out;pointer-events:none;background:rgba(0,229,255,.12);}
+ .pad.playing .cursor{transform:translate(-50%,-50%) scale(1);}
+ .cursor::before,.cursor::after{content:'';position:absolute;background:rgba(0,229,255,.35);}
+ .cursor::before{left:50%;top:-1000px;width:1px;height:2000px;transform:translateX(-50%);}
+ .cursor::after{top:50%;left:-1000px;height:1px;width:2000px;transform:translateY(-50%);}
+
+ /* Controls */
+ .controls{display:flex;flex-wrap:wrap;gap:12px;}
+ .ctl{background:var(--panel);border:1px solid #22223a;border-radius:12px;padding:12px 14px;
+ flex:1;min-width:150px;}
+ .ctl .lbl{font-size:10px;letter-spacing:2px;text-transform:uppercase;color:var(--muted);margin-bottom:9px;}
+ .waves{display:flex;gap:6px;}
+ .waves button{flex:1;background:#0d0d18;border:1px solid #2a2a44;color:var(--muted);
+ border-radius:8px;padding:8px 0;font-size:11px;cursor:pointer;transition:.15s;text-transform:capitalize;
+ display:flex;flex-direction:column;align-items:center;gap:4px;}
+ .waves button svg{width:26px;height:14px;}
+ .waves button:hover{border-color:var(--accent);color:var(--text);}
+ .waves button.on{background:linear-gradient(120deg,rgba(0,229,255,.2),rgba(255,61,129,.15));
+ border-color:var(--accent);color:var(--text);box-shadow:0 0 12px rgba(0,229,255,.25);}
+ .waves button.on svg{stroke:var(--accent);}
+ .waves button svg{stroke:currentColor;fill:none;stroke-width:2;}
+
+ .toggles{display:flex;gap:8px;flex-wrap:wrap;}
+ .tog{flex:1;min-width:80px;background:#0d0d18;border:1px solid #2a2a44;color:var(--muted);
+ border-radius:8px;padding:9px 6px;font-size:11px;letter-spacing:1px;cursor:pointer;
+ text-transform:uppercase;transition:.15s;display:flex;align-items:center;justify-content:center;gap:6px;}
+ .tog .led{width:7px;height:7px;border-radius:50%;background:#333;transition:.15s;}
+ .tog:hover{border-color:var(--accent2);}
+ .tog.on{color:var(--text);border-color:var(--accent2);background:rgba(255,61,129,.12);}
+ .tog.on .led{background:var(--accent2);box-shadow:0 0 8px var(--accent2);}
+
+ .foot{font-size:10px;color:#555;letter-spacing:1px;text-align:center;margin-top:2px;}
+</style>
+</head>
+<body>
+ <h1><span class="dot"></span> XY Synth Pad</h1>
+
+ <div class="wrap">
+ <div class="note-display" id="noteDisplay">
+ <div class="glow"></div>
+ <div class="note-main">
+ <span class="note-name" id="noteName">—</span>
+ <span class="note-oct" id="noteOct"></span>
+ </div>
+ <div class="readouts">
+ <div class="row">FREQ <b id="freqOut">— Hz</b></div>
+ <div class="row">CUTOFF <b id="cutOut">— Hz</b></div>
+ </div>
+ </div>
+
+ <div class="pad" id="pad">
+ <div class="grid"></div>
+ <span class="axis-label ax-x">Pitch →</span>
+ <span class="axis-label ax-y">Cutoff →</span>
+ <div class="hint">Drag anywhere to play</div>
+ <div class="cursor" id="cursor"></div>
+ </div>
+
+ <div class="controls">
+ <div class="ctl">
+ <div class="lbl">Waveform</div>
+ <div class="waves" id="waves">
+ <button data-w="sine" class="on"><svg viewBox="0 0 26 14"><path d="M1 7 C4 1,7 1,10 7 S16 13,19 7 22 1,25 7"/></svg>sine</button>
+ <button data-w="square"><svg viewBox="0 0 26 14"><path d="M1 12 V2 H9 V12 H17 V2 H25"/></svg>square</button>
+ <button data-w="sawtooth"><svg viewBox="0 0 26 14"><path d="M1 12 L8 2 V12 L15 2 V12 L22 2"/></svg>saw</button>
+ <button data-w="triangle"><svg viewBox="0 0 26 14"><path d="M1 12 L5 2 L9 12 L13 2 L17 12 L21 2 L25 12"/></svg>tri</button>
+ </div>
+ </div>
+
+ <div class="ctl">
+ <div class="lbl">Effects & Scale</div>
+ <div class="toggles">
+ <button class="tog" id="delayBtn"><span class="led"></span>Delay</button>
+ <button class="tog on" id="snapBtn"><span class="led"></span>Snap</button>
+ </div>
+ </div>
+ </div>
+
+ <div class="foot">WebAudio · no libraries · quantized to a pentatonic-friendly chromatic scale</div>
+ </div>
+
+<script>
+(function(){
+ "use strict";
+
+ // ---- Audio graph (lazily created) ----
+ var ac=null, osc=null, filter=null, amp=null, delay=null, feedback=null, delayWet=null, master=null;
+ var playing=false, delayOn=false, snapOn=true, waveform='sine';
+
+ var NOTES=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
+ // Playable pitch range across the X axis (in MIDI note numbers): C2 .. C6
+ var MIDI_LO=36, MIDI_HI=84;
+ var CUT_LO=120, CUT_HI=12000;
+
+ function midiToFreq(m){ return 440*Math.pow(2,(m-69)/12); }
+ function noteLabel(m){ var i=((Math.round(m)%12)+12)%12; return {name:NOTES[i], oct:Math.floor(Math.round(m)/12)-1}; }
+
+ function ensureAudio(){
+ if(ac) return;
+ ac=new (window.AudioContext||window.webkitAudioContext)();
+
+ osc=ac.createOscillator();
+ osc.type=waveform;
+
+ filter=ac.createBiquadFilter();
+ filter.type='lowpass';
+ filter.frequency.value=2000;
+ filter.Q.value=6;
+
+ amp=ac.createGain();
+ amp.gain.value=0; // silent until touched
+
+ master=ac.createGain();
+ master.gain.value=0.9;
+
+ // Delay send
+ delay=ac.createDelay(1.0);
+ delay.delayTime.value=0.28;
+ feedback=ac.createGain();
+ feedback.gain.value=0.38;
+ delayWet=ac.createGain();
+ delayWet.gain.value=0; // off by default
+
+ // Routing
+ osc.connect(filter);
+ filter.connect(amp);
+ amp.connect(master); // dry
+ amp.connect(delay); // feed delay
+ delay.connect(feedback);
+ feedback.connect(delay); // feedback loop
+ delay.connect(delayWet);
+ delayWet.connect(master); // wet
+ master.connect(ac.destination);
+
+ osc.start();
+ }
+
+ // ---- Interaction ----
+ var pad=document.getElementById('pad');
+ var cursor=document.getElementById('cursor');
+ var noteDisplay=document.getElementById('noteDisplay');
+ var noteName=document.getElementById('noteName');
+ var noteOct=document.getElementById('noteOct');
+ var freqOut=document.getElementById('freqOut');
+ var cutOut=document.getElementById('cutOut');
+
+ function setFromEvent(clientX,clientY){
+ var r=pad.getBoundingClientRect();
+ var x=(clientX-r.left)/r.width;
+ var y=(clientY-r.top)/r.height;
+ x=Math.min(1,Math.max(0,x));
+ y=Math.min(1,Math.max(0,y));
+
+ // X -> pitch
+ var midi=MIDI_LO+x*(MIDI_HI-MIDI_LO);
+ if(snapOn) midi=Math.round(midi);
+ var freq=midiToFreq(midi);
+
+ // Y -> cutoff (top = bright/high, bottom = dark/low) using exp scaling
+ var yy=1-y;
+ var cutoff=CUT_LO*Math.pow(CUT_HI/CUT_LO, yy);
+
+ var t=ac.currentTime;
+ osc.frequency.setTargetAtTime(freq, t, snapOn?0.012:0.02);
+ filter.frequency.setTargetAtTime(cutoff, t, 0.02);
+
+ // cursor
+ cursor.style.left=(x*100)+'%';
+ cursor.style.top=(y*100)+'%';
+
+ // display
+ var lbl=noteLabel(midi);
+ noteName.textContent=lbl.name;
+ noteOct.textContent=lbl.oct;
+ freqOut.textContent=freq.toFixed(1)+' Hz';
+ cutOut.textContent=Math.round(cutoff)+' Hz';
+ }
+
+ function start(clientX,clientY){
+ ensureAudio();
+ if(ac.state==='suspended') ac.resume();
+ playing=true;
+ pad.classList.add('playing');
+ noteDisplay.classList.add('active');
+ setFromEvent(clientX,clientY);
+ amp.gain.setTargetAtTime(0.28, ac.currentTime, 0.01);
+ }
+ function move(clientX,clientY){ if(playing) setFromEvent(clientX,clientY); }
+ function stop(){
+ if(!playing) return;
+ playing=false;
+ pad.classList.remove('playing');
+ noteDisplay.classList.remove('active');
+ if(amp) amp.gain.setTargetAtTime(0.0001, ac.currentTime, 0.05);
+ }
+
+ // Pointer events (covers mouse + touch + pen)
+ pad.addEventListener('pointerdown',function(e){
+ e.preventDefault();
+ pad.setPointerCapture&&pad.setPointerCapture(e.pointerId);
+ start(e.clientX,e.clientY);
+ });
+ pad.addEventListener('pointermove',function(e){ move(e.clientX,e.clientY); });
+ pad.addEventListener('pointerup',stop);
+ pad.addEventListener('pointercancel',stop);
+ pad.addEventListener('pointerleave',function(e){ if(e.pointerType==='mouse') stop(); });
+
+ // ---- Waveform selector ----
+ var waveBtns=document.querySelectorAll('#waves button');
+ waveBtns.forEach(function(b){
+ b.addEventListener('click',function(){
+ waveform=b.getAttribute('data-w');
+ waveBtns.forEach(function(x){x.classList.remove('on');});
+ b.classList.add('on');
+ if(osc) osc.type=waveform;
+ });
+ });
+
+ // ---- Delay toggle ----
+ var delayBtn=document.getElementById('delayBtn');
+ delayBtn.addEventListener('click',function(){
+ ensureAudio();
+ delayOn=!delayOn;
+ delayBtn.classList.toggle('on',delayOn);
+ delayWet.gain.setTargetAtTime(delayOn?0.5:0, ac.currentTime, 0.05);
+ });
+
+ // ---- Snap toggle ----
+ var snapBtn=document.getElementById('snapBtn');
+ snapBtn.addEventListener('click',function(){
+ snapOn=!snapOn;
+ snapBtn.classList.toggle('on',snapOn);
+ });
+
+ // Prevent page scroll/gestures interfering on touch devices
+ document.addEventListener('gesturestart',function(e){e.preventDefault();});
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/43dfd86f6460/claude-code.png b/data/artifacts/43dfd86f6460/claude-code.png
new file mode 100644
index 0000000..986bf24
Binary files /dev/null and b/data/artifacts/43dfd86f6460/claude-code.png differ
diff --git a/data/artifacts/43dfd86f6460/gemma3-12b.html b/data/artifacts/43dfd86f6460/gemma3-12b.html
new file mode 100644
index 0000000..7ba6b03
--- /dev/null
+++ b/data/artifacts/43dfd86f6460/gemma3-12b.html
@@ -0,0 +1,163 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>XY Pad Synth</title>
+<style>
+body { margin: 0; overflow: hidden; background-color: #222; color: #eee; font-family: sans-serif; }
+#container { width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; }
+canvas { border: 1px solid #555; background-color: #333; cursor: grab; }
+#info { position: absolute; bottom: 10px; left: 10px; font-size: 12px; }
+</style>
+</head>
+<body>
+
+<div id="container">
+ <canvas id="pad" width="400" height="400"></canvas>
+ <div id="info">Waveform: <span id="waveformDisplay">sine</span> | Delay: <span id="delayStatus">off</span> | Note: <span id="noteDisplay">C4</span></div>
+</div>
+
+<script>
+const canvas = document.getElementById('pad');
+const ctx = canvas.getContext('2d');
+const info = document.getElementById('info');
+const waveformDisplay = document.getElementById('waveformDisplay');
+const delayStatus = document.getElementById('delayStatus');
+const noteDisplay = document.getElementById('noteDisplay');
+
+let audioContext;
+let oscillator;
+let filter;
+let analyser;
+
+let x = 0.5; // Horizontal position (pitch)
+let y = 0.5; // Vertical position (filter cutoff)
+let isDragging = false;
+let lastX, lastY;
+
+const waveforms = ['sine', 'square', 'sawtooth', 'triangle'];
+let currentWaveformIndex = 0;
+let delayEnabled = false;
+
+
+function init() {
+ audioContext = new AudioContext();
+ oscillator = audioContext.createOscillator();
+ filter = audioContext.createFilter();
+ analyser = audioContext.createAnalyser();
+
+ oscillator.type = waveforms[currentWaveformIndex];
+ oscillator.frequency.setValueAtTime(440, audioContext.currentTime); // Start at A4 (440 Hz)
+
+ filter.frequency.setValueAtTime(2000, audioContext.currentTime); // Initial filter cutoff
+ filter.type = 'lowpass';
+
+ analyser.fftSize = 256;
+ analyser.smoothingTimeConstant = 0.8;
+
+ oscillator.connect(filter);
+ filter.connect(analyser);
+ analyser.connect(audioContext.destination);
+
+
+ canvas.addEventListener('mousedown', startDrag);
+ canvas.addEventListener('mouseup', stopDrag);
+ canvas.addEventListener('mouseleave', stopDrag);
+ canvas.addEventListener('mousemove', drag);
+
+ document.addEventListener('keydown', function(event) {
+ if (event.key === ' ') { // Spacebar to toggle waveform
+ toggleWaveform();
+ } else if (event.key === 'd') { // d to toggle delay
+ toggleDelay();
+ }
+ });
+
+ updateDisplay();
+}
+
+
+function startDrag(e) {
+ isDragging = true;
+ lastX = e.offsetX;
+ lastY = e.offsetY;
+ canvas.style.cursor = 'grabbing';
+}
+
+function stopDrag() {
+ isDragging = false;
+ canvas.style.cursor = 'grab';
+}
+
+function drag(e) {
+ if (!isDragging) return;
+
+ const dx = e.offsetX - lastX;
+ const dy = e.offsetY - lastY;
+
+ x = Math.max(0, Math.min(1, x + dx / canvas.width));
+ y = Math.max(0, Math.min(1, y - dy / canvas.height));
+
+ lastX = e.offsetX;
+ lastY = e.offsetY;
+
+ updateOscillator();
+ updateFilter();
+ updateDisplay();
+}
+
+
+
+function updateOscillator() {
+ const pitchShift = x * 12; // 12 semitones range for pitch shift
+ let freq = Math.pow(2, (pitchShift / 12)) * 440;
+
+ // Limit frequency to a reasonable range to avoid audio issues
+ freq = Math.max(20, Math.min(8000, freq));
+ oscillator.frequency.setValueAtTime(freq, audioContext.currentTime);
+}
+
+
+function updateFilter() {
+ const cutoff = 200 + y * 6800; // Cutoff range from 200Hz to 7000Hz
+ filter.frequency.setValueAtTime(cutoff, audioContext.currentTime);
+
+}
+
+function toggleWaveform() {
+ currentWaveformIndex = (currentWaveformIndex + 1) % waveforms.length;
+ oscillator.type = waveforms[currentWaveformIndex];
+ waveformDisplay.textContent = waveforms[currentWaveformIndex];
+ updateOscillator(); // Apply immediate change to oscillator
+}
+
+function toggleDelay() {
+ delayEnabled = !delayEnabled;
+ delayStatus.textContent = delayEnabled ? "on" : "off";
+}
+
+
+function updateDisplay() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Draw the pad background circle
+ ctx.beginPath();
+ ctx.arc(canvas.width / 2, canvas.height / 2, Math.min(canvas.width, canvas.height) / 2 - 10, 0, 2 * Math.PI);
+ ctx.fillStyle = '#444';
+ ctx.fill();
+
+ // Draw the current position marker
+ ctx.beginPath();
+ ctx.arc(x * canvas.width, y * canvas.height, 5, 0, 2 * Math.PI);
+ ctx.fillStyle = 'red';
+ ctx.fill();
+
+ const noteNumber = 69 + Math.round((x - 0.5) * 12); // C4 is note number 69
+ noteDisplay.textContent = String.fromCharCode(65 + (noteNumber % 12)) + (noteNumber / 12 | 0); //convert to musical notes
+}
+
+init();
+
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/43dfd86f6460/gemma3-12b.png b/data/artifacts/43dfd86f6460/gemma3-12b.png
new file mode 100644
index 0000000..21945cc
Binary files /dev/null and b/data/artifacts/43dfd86f6460/gemma3-12b.png differ
diff --git a/data/artifacts/43dfd86f6460/hermes3-8b.html b/data/artifacts/43dfd86f6460/hermes3-8b.html
new file mode 100644
index 0000000..8d25028
--- /dev/null
+++ b/data/artifacts/43dfd86f6460/hermes3-8b.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>XY Synth Pad</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ background-color: #f0f0f0;
+ }
+
+ canvas {
+ border: 1px solid #ccc;
+ display: block;
+ }
+
+ .pad {
+ width: 500px;
+ height: 300px;
+ position: relative;
+ margin: 20px auto;
+ background-color: #ddd;
+ cursor: crosshair;
+ }
+
+ .note {
+ color: #000;
+ font-size: 24px;
+ position: absolute;
+ bottom: 10px;
+ right: 10px;
+ }
+ </style>
+</head>
+<body>
+ <div class="pad" id="synthPad"></div>
+ <canvas id="noteVisual"></canvas>
+ <div class="note" id="currentNote"></div>
+
+ <script>
+ const synthPad = document.getElementById('synthPad');
+ const noteVisual = document.getElementById('noteVisual');
+ const currentNote = document.getElementById('currentNote');
+
+ const ctx = noteVisual.getContext('2d');
+ let oscillator;
+ let filter;
+
+ synthPad.addEventListener('mousemove', function(e) {
+ const rect = this.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ const pitch = Math.floor((x / this.offsetWidth) * 88);
+ const cutoff = Math.floor((y / this.offsetHeight) * 20000);
+
+ oscillator.frequency.value = pitch;
+ filter.frequency.value = cutoff;
+
+ drawNoteVisual(pitch, cutoff);
+ });
+
+ synthPad.addEventListener('click', function() {
+ oscillator.start();
+ filter.connect(context.destination);
+ });
+
+ synthPad.addEventListener('dblclick', function() {
+ oscillator.stop();
+ filter.disconnect(context.destination);
+ });
+
+ const context = new AudioContext();
+
+ oscillator = context.createOscillator();
+ oscillator.type = 'sawtooth';
+ filter = context.createBiquadFilter();
+ filter.type = 'lowpass';
+
+ synthPad.addEventListener('mousedown', function() {
+ oscillator.connect(filter);
+ });
+
+ synthPad.addEventListener('mouseup', function() {
+ if (!delay) {
+ oscillator.disconnect();
+ filter.disconnect();
+ }
+ });
+
+ const delay = false;
+
+ function drawNoteVisual(pitch, cutoff) {
+ ctx.clearRect(0, 0, noteVisual.width, noteVisual.height);
+
+ const pitchName = getPitchName(pitch);
+ const visualWidth = noteVisual.width * (pitch / 88);
+ const visualHeight = noteVisual.height * (cutoff / 20000);
+
+ ctx.fillStyle = 'rgba(255, 0, 0, 0.7)';
+ ctx.fillRect(noteVisual.width - visualWidth, noteVisual.height - visualHeight, visualWidth, visualHeight);
+
+ currentNote.innerText = pitchName;
+ }
+
+ function getPitchName(pitch) {
+ const notes = ['C', 'C♯/D♭', 'E♭/F', 'F♯/G♭', 'A', 'A♯/B♭', 'C'];
+ return notes[Math.floor(pitch / 12)];
+ }
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/43dfd86f6460/hermes3-8b.png b/data/artifacts/43dfd86f6460/hermes3-8b.png
new file mode 100644
index 0000000..f797525
Binary files /dev/null and b/data/artifacts/43dfd86f6460/hermes3-8b.png differ
diff --git a/data/artifacts/43dfd86f6460/hf-qwen-coder-32b.html b/data/artifacts/43dfd86f6460/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..0127fdd
--- /dev/null
+++ b/data/artifacts/43dfd86f6460/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>XY Synth Pad</title>
+<style>
+ body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #222; }
+ #pad { width: 300px; height: 300px; background-color: #444; position: relative; cursor: crosshair; }
+ #info { color: white; text-align: center; margin-top: 20px; font-family: Arial, sans-serif; }
+ select, button { margin: 5px; padding: 10px; background-color: #333; border: none; color: white; cursor: pointer; }
+</style>
+</head>
+<body>
+<div id="pad"></div>
+<div id="info">Drag on the pad to play. Pitch (X), Filter Cutoff (Y)</div>
+<select id="waveform">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="sawtooth">Sawtooth</option>
+ <option value="triangle">Triangle</option>
+</select>
+<button id="delayToggle">Delay Off</button>
+<script>
+const pad = document.getElementById('pad');
+const info = document.getElementById('info');
+const waveformSelect = document.getElementById('waveform');
+const delayToggle = document.getElementById('delayToggle');
+
+const audioContext = new (window.AudioContext || window.webkitAudioContext)();
+let oscillator = null;
+let filter = audioContext.createBiquadFilter();
+filter.type = 'lowpass';
+let gainNode = audioContext.createGain();
+gainNode.gain.value = 0.5;
+
+let delay = audioContext.createDelay();
+delay.delayTime.value = 0.3;
+let feedback = audioContext.createGain();
+feedback.gain.value = 0.4;
+delay.connect(feedback);
+feedback.connect(delay);
+
+filter.connect(gainNode);
+gainNode.connect(audioContext.destination);
+
+function startOscillator() {
+ oscillator = audioContext.createOscillator();
+ oscillator.type = waveformSelect.value;
+ oscillator.connect(filter);
+ oscillator.start();
+}
+
+pad.addEventListener('mousedown', () => {
+ if (!oscillator) startOscillator();
+});
+
+pad.addEventListener('mousemove', (e) => {
+ if (oscillator) {
+ const rect = pad.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+ oscillator.frequency.value = 20 + (x / rect.width) * 1000; // Pitch range: 20Hz to 1020Hz
+ filter.frequency.value = 200 + (y / rect.height) * 3000; // Cutoff range: 200Hz to 3200Hz
+ info.textContent = `Pitch: ${oscillator.frequency.value.toFixed(2)}Hz, Filter Cutoff: ${filter.frequency.value.toFixed(2)}Hz`;
+ }
+});
+
+pad.addEventListener('mouseup', () => {
+ if (oscillator) {
+ oscillator.stop();
+ oscillator.disconnect();
+ oscillator = null;
+ }
+});
+
+waveformSelect.addEventListener('change', () => {
+ if (oscillator) {
+ oscillator.type = waveformSelect.value;
+ }
+});
+
+delayToggle.addEventListener('click', () => {
+ if (gainNode.disconnect(delay)) {
+ gainNode.connect(delay);
+ delay.connect(feedback);
+ feedback.connect(delay);
+ delayToggle.textContent = 'Delay On';
+ } else {
+ gainNode.disconnect(delay);
+ delayToggle.textContent = 'Delay Off';
+ }
+});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/43dfd86f6460/hf-qwen-coder-32b.png b/data/artifacts/43dfd86f6460/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..93be747
Binary files /dev/null and b/data/artifacts/43dfd86f6460/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/43dfd86f6460/qwen25-7b.html b/data/artifacts/43dfd86f6460/qwen25-7b.html
new file mode 100644
index 0000000..41702d9
--- /dev/null
+++ b/data/artifacts/43dfd86f6460/qwen25-7b.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>XY Synth Pad</title>
+ <style>
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ background-color: #f0f0f0;
+ margin: 0;
+ }
+ canvas {
+ border: 2px solid black;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="xyPad" width="500" height="500"></canvas>
+
+ <script>
+ const canvas = document.getElementById('xyPad');
+ const ctx = canvas.getContext('2d');
+
+ let oscillator, gainNode;
+
+ function setupAudio() {
+ oscillator = new AudioContext().createOscillator();
+ gainNode = new GainNode(new AudioContext());
+ oscillator.type = 'sine';
+ oscillator.connect(gainNode);
+ gainNode.connect(new AudioContext().destination);
+ gainNode.gain.value = 0.5;
+ }
+
+ function drawPad() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.fillStyle = '#ccc';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ const pitch = Math.min(4096, (event.offsetX / canvas.width) * 8192 + 500);
+ const filterCutoff = event.offsetY / canvas.height;
+
+ oscillator.frequency.setValueAtTime(pitch, ctx.audioContext.currentTime);
+ gainNode.gain.setValueAtTime(filterCutoff, ctx.audioContext.currentTime);
+
+ ctx.beginPath();
+ ctx.arc(event.offsetX, event.offsetY, 15, 0, Math.PI * 2);
+ ctx.fillStyle = 'red';
+ ctx.fill();
+
+ const waveformVisual = document.createElement('div');
+ waveformVisual.style.width = `${canvas.width / 8}px`;
+ waveformVisual.style.height = `100%`;
+ waveformVisual.style.position = 'absolute';
+ waveformVisual.style.left = `${(event.offsetX - canvas.width / 8) * 2}px`;
+ waveformVisual.style.backgroundColor = 'blue';
+ document.body.appendChild(waveformVisual);
+ }
+
+ setupAudio();
+ canvas.addEventListener('mousemove', drawPad);
+
+ let delayEnabled = false;
+ const delayTime = 0.5;
+
+ function toggleDelay() {
+ delayEnabled = !delayEnabled;
+ oscillator.connect(gainNode, 0, delayEnabled ? delayTime : 0);
+ }
+
+ document.body.addEventListener('click', () => {
+ if (Math.random() > 0.5) {
+ toggleDelay();
+ }
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/43dfd86f6460/qwen25-7b.png b/data/artifacts/43dfd86f6460/qwen25-7b.png
new file mode 100644
index 0000000..e1ef9c1
Binary files /dev/null and b/data/artifacts/43dfd86f6460/qwen25-7b.png differ
diff --git a/data/artifacts/43dfd86f6460/qwen3-14b.html b/data/artifacts/43dfd86f6460/qwen3-14b.html
new file mode 100644
index 0000000..58087e8
--- /dev/null
+++ b/data/artifacts/43dfd86f6460/qwen3-14b.html
@@ -0,0 +1,157 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>XY Synth Pad</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #111;
+ color: #fff;
+ font-family: sans-serif;
+ }
+ #container {
+ position: relative;
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ }
+ #pad {
+ position: relative;
+ width: 400px;
+ height: 400px;
+ background: #222;
+ border: 2px solid #555;
+ border-radius: 10px;
+ cursor: crosshair;
+ overflow: hidden;
+ }
+ #noteDisplay {
+ position: absolute;
+ top: 50px;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 24px;
+ font-weight: bold;
+ text-shadow: 0 0 10px #0f0;
+ }
+ #controls {
+ margin-top: 20px;
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ flex-wrap: wrap;
+ }
+ #controls label {
+ font-size: 14px;
+ }
+ #waveformSelect, #delayToggle {
+ padding: 5px 10px;
+ border: 1px solid #444;
+ background: #333;
+ color: #fff;
+ border-radius: 5px;
+ }
+ #waveformSelect {
+ flex: 1;
+ }
+ </style>
+</head>
+<body>
+ <div id="container">
+ <div id="noteDisplay">-</div>
+ <div id="pad"></div>
+ <div id="controls">
+ <label for="waveformSelect">Waveform:</label>
+ <select id="waveformSelect">
+ <option value="sine">Sine</option>
+ <option value="square">Square</option>
+ <option value="sawtooth">Sawtooth</option>
+ <option value="triangle">Triangle</option>
+ </select>
+ <label for="delayToggle">Delay:</label>
+ <button id="delayToggle">Off</button>
+ </div>
+ </div>
+ <script>
+ const AudioContext = window.AudioContext || window.webkitAudioContext;
+ const audioCtx = new AudioContext();
+ const pad = document.getElementById('pad');
+ const noteDisplay = document.getElementById('noteDisplay');
+ const waveformSelect = document.getElementById('waveformSelect');
+ const delayToggle = document.getElementById('delayToggle');
+
+ let oscillator, filter, gainNode, delayNode, filterNode, isPlaying = false;
+
+ function initAudio() {
+ oscillator = audioCtx.createOscillator();
+ filterNode = audioCtx.createBiquadFilter();
+ gainNode = audioCtx.createGain();
+ delayNode = audioCtx.createDelay(1);
+
+ oscillator.type = waveformSelect.value;
+ oscillator.frequency.value = 440;
+ filterNode.type = 'lowpass';
+ filterNode.frequency.value = 2000;
+ delayNode.delayTime.value = 0.5;
+ delayNode.feedback.value = 0.5;
+
+ oscillator.connect(filterNode);
+ filterNode.connect(gainNode);
+ gainNode.connect(audioCtx.destination);
+ gainNode.connect(delayNode);
+ delayNode.connect(filterNode);
+
+ oscillator.start();
+ }
+
+ function updateNote(x, y) {
+ const freq = 220 * Math.pow(2, x / 400);
+ const cutoff = 20 * Math.pow(2, y / 400);
+ oscillator.frequency.value = freq;
+ filterNode.frequency.value = cutoff;
+ noteDisplay.textContent = `${Math.round(freq)} Hz`;
+ }
+
+ function toggleDelay() {
+ if (delayToggle.textContent === 'Off') {
+ delayToggle.textContent = 'On';
+ gainNode.connect(delayNode);
+ } else {
+ delayToggle.textContent = 'Off';
+ gainNode.disconnect(delayNode);
+ }
+ }
+
+ pad.addEventListener('mousedown', (e) => {
+ isPlaying = true;
+ updateNote(e.offsetX, e.offsetY);
+ });
+
+ pad.addEventListener('mousemove', (e) => {
+ if (isPlaying) {
+ updateNote(e.offsetX, e.offsetY);
+ }
+ });
+
+ pad.addEventListener('mouseup', () => {
+ isPlaying = false;
+ noteDisplay.textContent = '-';
+ });
+
+ waveformSelect.addEventListener('change', (e) => {
+ oscillator.type = e.target.value;
+ });
+
+ delayToggle.addEventListener('click', toggleDelay);
+
+ initAudio();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/43dfd86f6460/qwen3-14b.png b/data/artifacts/43dfd86f6460/qwen3-14b.png
new file mode 100644
index 0000000..805f7d9
Binary files /dev/null and b/data/artifacts/43dfd86f6460/qwen3-14b.png differ
diff --git a/data/artifacts/48026228790d/gemma3-12b.png b/data/artifacts/48026228790d/gemma3-12b.png
new file mode 100644
index 0000000..1811546
Binary files /dev/null and b/data/artifacts/48026228790d/gemma3-12b.png differ
diff --git a/data/artifacts/5dbb1c6732ae/claude-code.html b/data/artifacts/5dbb1c6732ae/claude-code.html
new file mode 100644
index 0000000..bd0edfc
--- /dev/null
+++ b/data/artifacts/5dbb1c6732ae/claude-code.html
@@ -0,0 +1,363 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Constellation Clock</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ overflow: hidden;
+ background: #05060f;
+ }
+ #sky {
+ display: block;
+ width: 100vw;
+ height: 100vh;
+ }
+ #digital {
+ position: fixed;
+ right: 22px;
+ bottom: 18px;
+ font-family: "Courier New", ui-monospace, monospace;
+ color: rgba(200, 220, 255, 0.85);
+ letter-spacing: 3px;
+ font-size: 26px;
+ text-shadow: 0 0 8px rgba(120,170,255,0.6), 0 0 18px rgba(80,120,255,0.35);
+ user-select: none;
+ z-index: 5;
+ }
+ #digital .date {
+ display: block;
+ font-size: 12px;
+ letter-spacing: 4px;
+ text-align: right;
+ margin-top: 4px;
+ color: rgba(150, 180, 230, 0.6);
+ }
+ #label {
+ position: fixed;
+ left: 22px;
+ top: 16px;
+ font-family: "Courier New", ui-monospace, monospace;
+ color: rgba(160, 190, 240, 0.5);
+ font-size: 12px;
+ letter-spacing: 4px;
+ text-transform: uppercase;
+ user-select: none;
+ z-index: 5;
+ }
+</style>
+</head>
+<body>
+<canvas id="sky"></canvas>
+<div id="label">Celestial Chronometer</div>
+<div id="digital"><span id="clock">00:00:00</span><span class="date" id="date"></span></div>
+
+<script>
+(function () {
+ "use strict";
+
+ const canvas = document.getElementById("sky");
+ const ctx = canvas.getContext("2d");
+ const clockEl = document.getElementById("clock");
+ const dateEl = document.getElementById("date");
+
+ let W = 0, H = 0, CX = 0, CY = 0, DPR = 1;
+
+ function resize() {
+ DPR = Math.min(window.devicePixelRatio || 1, 2);
+ W = window.innerWidth;
+ H = window.innerHeight;
+ canvas.width = Math.floor(W * DPR);
+ canvas.height = Math.floor(H * DPR);
+ canvas.style.width = W + "px";
+ canvas.style.height = H + "px";
+ ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
+ CX = W / 2;
+ CY = H / 2;
+ buildStars();
+ }
+
+ // ---------- Background starfield ----------
+ let bgStars = [];
+ function buildStars() {
+ bgStars = [];
+ const count = Math.round((W * H) / 5200);
+ for (let i = 0; i < count; i++) {
+ bgStars.push({
+ x: Math.random() * W,
+ y: Math.random() * H,
+ r: Math.random() * 1.3 + 0.2,
+ base: Math.random() * 0.5 + 0.2,
+ amp: Math.random() * 0.5 + 0.1,
+ speed: Math.random() * 2 + 0.5,
+ phase: Math.random() * Math.PI * 2,
+ hue: 210 + Math.random() * 40
+ });
+ }
+ }
+
+ function drawBackground(t) {
+ // subtle vertical nebula gradient
+ const g = ctx.createRadialGradient(CX, CY, 0, CX, CY, Math.max(W, H) * 0.75);
+ g.addColorStop(0, "#0a1230");
+ g.addColorStop(0.5, "#070a1c");
+ g.addColorStop(1, "#03040c");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, W, H);
+
+ for (let i = 0; i < bgStars.length; i++) {
+ const s = bgStars[i];
+ const tw = s.base + s.amp * Math.sin(t * s.speed + s.phase);
+ const a = Math.max(0, Math.min(1, tw));
+ ctx.beginPath();
+ ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
+ ctx.fillStyle = "hsla(" + s.hue + ",70%,85%," + a + ")";
+ ctx.fill();
+ }
+ }
+
+ // ---------- Constellation hand definitions ----------
+ // Each hand is a chain of stars at given radius fractions along the hand.
+ // Points: {r: fraction of handRadius, o: sideways offset fraction, mag: star size}
+ const secondShape = [
+ { r: 0.00, o: 0.00, mag: 2.0 },
+ { r: 0.30, o: 0.05, mag: 1.6 },
+ { r: 0.55, o: -0.05, mag: 1.8 },
+ { r: 0.78, o: 0.04, mag: 1.5 },
+ { r: 0.95, o: 0.00, mag: 2.6 }
+ ];
+ const minuteShape = [
+ { r: 0.00, o: 0.00, mag: 2.4 },
+ { r: 0.22, o: 0.07, mag: 2.0 },
+ { r: 0.42, o: -0.06, mag: 2.2 },
+ { r: 0.60, o: 0.08, mag: 2.0 },
+ { r: 0.78, o: -0.04, mag: 2.4 },
+ { r: 0.92, o: 0.05, mag: 3.2 }
+ ];
+ const hourShape = [
+ { r: 0.00, o: 0.00, mag: 2.8 },
+ { r: 0.25, o: 0.10, mag: 2.4 },
+ { r: 0.30, o: -0.10, mag: 2.4 },
+ { r: 0.52, o: 0.06, mag: 2.6 },
+ { r: 0.70, o: -0.08, mag: 2.6 },
+ { r: 0.88, o: 0.00, mag: 3.6 }
+ ];
+
+ function drawConstellation(shape, angle, handRadius, color, glow, t, twinkle) {
+ // angle: 0 = 12 o'clock (up). rotate coordinate.
+ const ca = Math.cos(angle - Math.PI / 2);
+ const sa = Math.sin(angle - Math.PI / 2);
+ const pts = [];
+ for (let i = 0; i < shape.length; i++) {
+ const p = shape[i];
+ const along = p.r * handRadius;
+ const side = p.o * handRadius;
+ // local coords: along axis + perpendicular offset
+ const lx = along;
+ const ly = side;
+ const x = CX + lx * ca - ly * sa;
+ const y = CY + lx * sa + ly * ca;
+ pts.push({ x: x, y: y, mag: p.mag });
+ }
+
+ // connecting lines
+ ctx.save();
+ ctx.lineWidth = 1.2;
+ ctx.strokeStyle = color.line;
+ ctx.shadowColor = color.glow;
+ ctx.shadowBlur = glow;
+ ctx.beginPath();
+ ctx.moveTo(pts[0].x, pts[0].y);
+ for (let i = 1; i < pts.length; i++) ctx.lineTo(pts[i].x, pts[i].y);
+ ctx.stroke();
+ ctx.restore();
+
+ // stars
+ for (let i = 0; i < pts.length; i++) {
+ const p = pts[i];
+ const tw = twinkle ? (0.75 + 0.25 * Math.sin(t * 3 + i * 1.7)) : 1;
+ const rad = p.mag * tw;
+ ctx.save();
+ ctx.shadowColor = color.glow;
+ ctx.shadowBlur = glow * 1.4;
+ const grd = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, rad * 3);
+ grd.addColorStop(0, color.core);
+ grd.addColorStop(0.4, color.star);
+ grd.addColorStop(1, "rgba(0,0,0,0)");
+ ctx.fillStyle = grd;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, rad * 3, 0, Math.PI * 2);
+ ctx.fill();
+ // bright core
+ ctx.shadowBlur = 0;
+ ctx.fillStyle = color.core;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, rad * 0.8, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+ return pts;
+ }
+
+ // ---------- Hour ticks as faint stars around the dial ----------
+ function drawDialStars(t) {
+ const R = Math.min(W, H) * 0.44;
+ for (let i = 0; i < 12; i++) {
+ const a = (i / 12) * Math.PI * 2 - Math.PI / 2;
+ const x = CX + Math.cos(a) * R;
+ const y = CY + Math.sin(a) * R;
+ const tw = 0.5 + 0.5 * Math.sin(t * 1.5 + i);
+ const big = (i % 3 === 0);
+ ctx.beginPath();
+ ctx.arc(x, y, big ? 2.4 : 1.4, 0, Math.PI * 2);
+ ctx.fillStyle = "rgba(180,205,255," + (0.35 + 0.35 * tw) + ")";
+ ctx.shadowColor = "rgba(140,180,255,0.8)";
+ ctx.shadowBlur = big ? 10 : 5;
+ ctx.fill();
+ ctx.shadowBlur = 0;
+ }
+ // center star (the pole)
+ const cg = ctx.createRadialGradient(CX, CY, 0, CX, CY, 14);
+ cg.addColorStop(0, "rgba(255,255,255,0.95)");
+ cg.addColorStop(0.4, "rgba(180,210,255,0.6)");
+ cg.addColorStop(1, "rgba(0,0,0,0)");
+ ctx.fillStyle = cg;
+ ctx.beginPath();
+ ctx.arc(CX, CY, 14, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ // ---------- Shooting stars ----------
+ let shootings = [];
+ let nextShoot = 2 + Math.random() * 4;
+ function spawnShoot() {
+ const startX = Math.random() * W;
+ const startY = Math.random() * H * 0.5;
+ const angle = Math.PI * 0.15 + Math.random() * Math.PI * 0.2; // downward-ish
+ const speed = 500 + Math.random() * 400;
+ shootings.push({
+ x: startX,
+ y: startY,
+ vx: Math.cos(angle) * speed * (Math.random() < 0.5 ? 1 : -1),
+ vy: Math.sin(angle) * speed,
+ life: 0,
+ maxLife: 0.9 + Math.random() * 0.5,
+ len: 80 + Math.random() * 120
+ });
+ }
+ function updateShoot(dt) {
+ nextShoot -= dt;
+ if (nextShoot <= 0) {
+ spawnShoot();
+ nextShoot = 4 + Math.random() * 7;
+ }
+ for (let i = shootings.length - 1; i >= 0; i--) {
+ const s = shootings[i];
+ s.life += dt;
+ s.x += s.vx * dt;
+ s.y += s.vy * dt;
+ if (s.life > s.maxLife) shootings.splice(i, 1);
+ }
+ }
+ function drawShoot() {
+ for (let i = 0; i < shootings.length; i++) {
+ const s = shootings[i];
+ const prog = s.life / s.maxLife;
+ const alpha = Math.sin(prog * Math.PI); // fade in/out
+ const mag = Math.hypot(s.vx, s.vy) || 1;
+ const ux = s.vx / mag, uy = s.vy / mag;
+ const tx = s.x - ux * s.len;
+ const ty = s.y - uy * s.len;
+ const grd = ctx.createLinearGradient(s.x, s.y, tx, ty);
+ grd.addColorStop(0, "rgba(255,255,255," + (0.9 * alpha) + ")");
+ grd.addColorStop(1, "rgba(255,255,255,0)");
+ ctx.strokeStyle = grd;
+ ctx.lineWidth = 2;
+ ctx.shadowColor = "rgba(200,220,255,0.9)";
+ ctx.shadowBlur = 8;
+ ctx.beginPath();
+ ctx.moveTo(s.x, s.y);
+ ctx.lineTo(tx, ty);
+ ctx.stroke();
+ ctx.shadowBlur = 0;
+ // head
+ ctx.beginPath();
+ ctx.arc(s.x, s.y, 2, 0, Math.PI * 2);
+ ctx.fillStyle = "rgba(255,255,255," + alpha + ")";
+ ctx.fill();
+ }
+ }
+
+ // ---------- Main loop ----------
+ const colHour = {
+ line: "rgba(255,200,120,0.45)",
+ glow: "rgba(255,180,90,0.9)",
+ star: "rgba(255,190,110,0.9)",
+ core: "rgba(255,240,210,1)"
+ };
+ const colMinute = {
+ line: "rgba(140,200,255,0.5)",
+ glow: "rgba(120,180,255,0.9)",
+ star: "rgba(150,200,255,0.9)",
+ core: "rgba(235,245,255,1)"
+ };
+ const colSecond = {
+ line: "rgba(180,255,220,0.45)",
+ glow: "rgba(140,255,200,0.85)",
+ star: "rgba(170,255,210,0.9)",
+ core: "rgba(240,255,250,1)"
+ };
+
+ let lastTime = null;
+
+ function frame(now) {
+ if (lastTime === null) lastTime = now;
+ let dt = (now - lastTime) / 1000;
+ if (dt > 0.1) dt = 0.1;
+ lastTime = now;
+ const t = now / 1000;
+
+ const d = new Date();
+ const ms = d.getMilliseconds();
+ const sec = d.getSeconds() + ms / 1000;
+ const min = d.getMinutes() + sec / 60;
+ const hr = (d.getHours() % 12) + min / 60;
+
+ const secAngle = (sec / 60) * Math.PI * 2;
+ const minAngle = (min / 60) * Math.PI * 2;
+ const hrAngle = (hr / 12) * Math.PI * 2;
+
+ const base = Math.min(W, H);
+ const hourR = base * 0.20;
+ const minR = base * 0.33;
+ const secR = base * 0.40;
+
+ drawBackground(t);
+ drawDialStars(t);
+
+ updateShoot(dt);
+ drawShoot();
+
+ drawConstellation(hourShape, hrAngle, hourR, colHour, 16, t, true);
+ drawConstellation(minuteShape, minAngle, minR, colMinute, 14, t, true);
+ drawConstellation(secondShape, secAngle, secR, colSecond, 12, t, true);
+
+ // digital readout
+ const pad = (n) => (n < 10 ? "0" + n : "" + n);
+ clockEl.textContent = pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + pad(d.getSeconds());
+ dateEl.textContent = d.toLocaleDateString(undefined, { weekday: "short", month: "short", day: "numeric" });
+
+ requestAnimationFrame(frame);
+ }
+
+ window.addEventListener("resize", resize);
+ resize();
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/5dbb1c6732ae/claude-code.png b/data/artifacts/5dbb1c6732ae/claude-code.png
new file mode 100644
index 0000000..1b9b8d7
Binary files /dev/null and b/data/artifacts/5dbb1c6732ae/claude-code.png differ
diff --git a/data/artifacts/5dbb1c6732ae/gemma3-12b.html b/data/artifacts/5dbb1c6732ae/gemma3-12b.html
new file mode 100644
index 0000000..f754e47
--- /dev/null
+++ b/data/artifacts/5dbb1c6732ae/gemma3-12b.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Star Clock</title>
+<style>
+body { margin: 0; overflow: hidden; background-color: black; }
+canvas { display: block; }
+#digitalTime {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ font-family: monospace;
+ font-size: 24px;
+ color: white;
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
+}
+</style>
+</head>
+<body>
+
+<div id="digitalTime"></div>
+<canvas id="starClock" width="600" height="600"></canvas>
+
+<script>
+const canvas = document.getElementById('starClock');
+const ctx = canvas.getContext('2d');
+const digitalTimeDisplay = document.getElementById('digitalTime');
+
+const numStars = 200;
+const stars = [];
+
+function init() {
+ for (let i = 0; i < numStars; i++) {
+ stars.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ z: Math.random() * 100, // Depth for twinkling effect
+ size: Math.random() * 2 + 0.5
+ });
+ }
+}
+
+function drawStars() {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.95)'; // Dark background
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ ctx.fillStyle = 'white';
+ for (const star of stars) {
+ let alpha = Math.max(0.1, 1 - star.z / 100 ); // Twinkling based on depth
+ ctx.globalAlpha = alpha;
+ ctx.beginPath();
+ ctx.arc(star.x, star.y, star.size, 0, Math.PI * 2);
+ ctx.closePath();
+ ctx.fill();
+ ctx.globalAlpha = 1; // Reset globalAlpha after each star
+ }
+}
+
+
+function drawClockHands() {
+ const now = new Date();
+ const hour = now.getHours() % 12;
+ const minute = now.getMinutes();
+ const second = now.getSeconds();
+
+ // Hour hand (Constellation: Ursa Major - Big Dipper)
+ drawConstellation(hour * 30, true);
+
+ // Minute hand (Constellation: Orion)
+ drawConstellation(minute * 5, false);
+
+ // Second hand (Constellation: Cassiopeia)
+ drawConstellation(second * 6, true); // Rotate slightly differently
+}
+
+
+function drawConstellation(angle, isHourHand) {
+ const numPoints = 7;
+ const radius = isHourHand ? 150 : 200;
+ const starSize = isHourHand ? 3 : 2;
+
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)'; // Bright white for hands
+ ctx.lineWidth = 1;
+
+
+ ctx.beginPath();
+ for (let i = 0; i < numPoints; i++) {
+ const starAngle = angle + i * (360 / numPoints);
+
+ const x = canvas.width / 2 + Math.cos(starAngle * Math.PI / 180) * radius;
+ const y = canvas.height / 2 + Math.sin(starAngle * Math.PI / 180) * radius;
+
+
+ if (i === 0) {
+ ctx.moveTo(x,y);
+ } else {
+ ctx.lineTo(x,y);
+ }
+
+ }
+ ctx.stroke();
+}
+
+
+
+function drawShootingStar() {
+ const chance = Math.random();
+ if (chance < 0.01) { // Adjust frequency as needed
+ const startX = Math.random() * canvas.width;
+ const startY = Math.random() * canvas.height;
+ const endX = startX + (Math.random() * 50 - 25);
+ const endY = startY + (Math.random() * 30 - 15);
+
+ ctx.beginPath();
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
+ ctx.lineWidth = 2;
+ ctx.moveTo(startX, startY);
+ ctx.lineTo(endX, endY);
+ ctx.stroke();
+ }
+}
+
+function updateDigitalTime() {
+ const now = new Date();
+ let hours = now.getHours().toString().padStart(2, '0');
+ let minutes = now.getMinutes().toString().padStart(2, '0');
+ let seconds = now.getSeconds().toString().padStart(2, '0');
+
+ digitalTimeDisplay.textContent = `${hours}:${minutes}:${seconds}`;
+}
+
+
+function animate() {
+ drawStars();
+ drawClockHands();
+ drawShootingStar();
+ updateDigitalTime();
+ requestAnimationFrame(animate);
+}
+
+init();
+animate();
+
+
+
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/5dbb1c6732ae/gemma3-12b.png b/data/artifacts/5dbb1c6732ae/gemma3-12b.png
new file mode 100644
index 0000000..317e997
Binary files /dev/null and b/data/artifacts/5dbb1c6732ae/gemma3-12b.png differ
diff --git a/data/artifacts/5dbb1c6732ae/hermes3-8b.html b/data/artifacts/5dbb1c6732ae/hermes3-8b.html
new file mode 100644
index 0000000..e7acaea
--- /dev/null
+++ b/data/artifacts/5dbb1c6732ae/hermes3-8b.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Live Star Map Clock</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ }
+
+ canvas {
+ display: block;
+ background-color: #000;
+ }
+
+ .time {
+ position: absolute;
+ bottom: 10px;
+ right: 10px;
+ color: #fff;
+ font-family: monospace;
+ font-size: 16px;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="sky" width="800" height="400"></canvas>
+ <div class="time" id="digitalTime"></div>
+
+ <script>
+ const canvas = document.getElementById('sky');
+ const ctx = canvas.getContext('2d');
+
+ const stars = [];
+ for (let i = 0; i < 200; i++) {
+ stars.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ size: Math.random() * 3 + 1
+ });
+ }
+
+ function drawStars() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = 0; i < stars.length; i++) {
+ const star = stars[i];
+ ctx.beginPath();
+ ctx.arc(star.x, star.y, star.size, 0, Math.PI * 2);
+ ctx.fillStyle = `rgba(255, 255, 255, ${Math.random() * 0.1 + 0.05})`;
+ ctx.fill();
+ }
+ }
+
+ let time = new Date();
+
+ function updateTime() {
+ time = new Date();
+ const hours = time.getHours();
+ const minutes = time.getMinutes();
+ const seconds = time.getSeconds();
+
+ const hourStarX = (hours % 12) * 60;
+ const hourStarY = (minutes % 60) - 10;
+ const minuteStarX = (minutes % 60) * 2;
+ const minuteStarY = (seconds % 60) - 5;
+
+ drawStars();
+ document.getElementById('digitalTime').textContent = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+
+ requestAnimationFrame(updateTime);
+
+ if (Math.random() < 0.0005) {
+ const shootingStar = { x: Math.random() * canvas.width, y: Math.random() * canvas.height, size: 3 };
+ stars.push(shootingStar);
+ }
+ }
+
+ updateTime();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/5dbb1c6732ae/hermes3-8b.png b/data/artifacts/5dbb1c6732ae/hermes3-8b.png
new file mode 100644
index 0000000..e3f7577
Binary files /dev/null and b/data/artifacts/5dbb1c6732ae/hermes3-8b.png differ
diff --git a/data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.html b/data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..c50d257
--- /dev/null
+++ b/data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Star Clock</title>
+<style>
+body,html { margin: 0; padding: 0; overflow: hidden; width: 100%; height: 100%; background-color: #000; color: white; font-family: Arial, sans-serif; }
+canvas { display: block; }
+#time { position: absolute; top: 10px; left: 10px; font-size: 24px; }
+</style>
+</head>
+<body>
+<canvas id="starClock"></canvas>
+<div id="time">00:00:00</div>
+<script>
+const canvas = document.getElementById('starClock');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+let stars = Array.from({length: 100}, () => ({x: Math.random() * canvas.width, y: Math.random() * canvas.height, twinkle: Math.random()}));
+let shootingStar = {active: false, x: 0, y: 0, endX: 0, endY: 0, progress: 0};
+
+function drawStars() {
+ stars.forEach(star => {
+ ctx.fillStyle = `rgba(255, 255, 255, ${star.twinkle})`;
+ ctx.beginPath();
+ ctx.arc(star.x, star.y, Math.random() * 1.5 + 0.5, 0, Math.PI * 2);
+ ctx.fill();
+ });
+}
+
+function drawShootingStar() {
+ if (shootingStar.active) {
+ shootingStar.progress += 0.03;
+ let x = shootingStar.x + (shootingStar.endX - shootingStar.x) * shootingStar.progress;
+ let y = shootingStar.y + (shootingStar.endY - shootingStar.y) * shootingStar.progress;
+ ctx.strokeStyle = `rgba(255, 255, 0, ${1 - shootingStar.progress})`;
+ ctx.lineWidth = 3;
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(shootingStar.x, shootingStar.y);
+ ctx.stroke();
+ if (shootingStar.progress > 1) shootingStar.active = false;
+ }
+}
+
+function drawConstellation(points, color) {
+ ctx.strokeStyle = color;
+ ctx.lineWidth = 2;
+ ctx.beginPath();
+ points.forEach((point, index) => {
+ if (index === 0) ctx.moveTo(point.x, point.y);
+ else ctx.lineTo(point.x, point.y);
+ });
+ ctx.closePath();
+ ctx.stroke();
+}
+
+function drawHands() {
+ const now = new Date();
+ document.getElementById('time').textContent = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
+ let secondAngle = (now.getSeconds() / 60) * Math.PI * 2 - Math.PI / 2;
+ let minuteAngle = (now.getMinutes() / 60) * Math.PI * 2 - Math.PI / 2;
+ let hourAngle = ((now.getHours() % 12) / 12) * Math.PI * 2 - Math.PI / 2;
+
+ const center = {x: canvas.width / 2, y: canvas.height / 2};
+ const secondHand = [{x: center.x, y: center.y}, {x: center.x + Math.cos(secondAngle) * (canvas.width / 3), y: center.y + Math.sin(secondAngle) * (canvas.height / 3)}];
+ const minuteHand = [{x: center.x, y: center.y}, {x: center.x + Math.cos(minuteAngle) * (canvas.width / 4), y: center.y + Math.sin(minuteAngle) * (canvas.height / 4)}];
+ const hourHand = [{x: center.x, y: center.y}, {x: center.x + Math.cos(hourAngle) * (canvas.width / 5), y: center.y + Math.sin(hourAngle) * (canvas.height / 5)}];
+
+ drawConstellation(secondHand, 'red');
+ drawConstellation(minuteHand, 'green');
+ drawConstellation(hourHand, 'blue');
+}
+
+function spawnShootingStar() {
+ if (!shootingStar.active && Math.random() < 0.01) {
+ shootingStar.x = Math.random() * canvas.width;
+ shootingStar.y = Math.random() * canvas.height;
+ shootingStar.endX = shootingStar.x + (Math.random() - 0.5) * canvas.width / 2;
+ shootingStar.endY = shootingStar.y + (Math.random() - 0.5) * canvas.height / 2;
+ shootingStar.progress = 0;
+ shootingStar.active = true;
+ }
+}
+
+function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ drawStars();
+ drawHands();
+ drawShootingStar();
+ spawnShootingStar();
+ requestAnimationFrame(animate);
+}
+
+window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+});
+
+animate();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.png b/data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..5e611e3
Binary files /dev/null and b/data/artifacts/5dbb1c6732ae/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/5dbb1c6732ae/qwen25-7b.html b/data/artifacts/5dbb1c6732ae/qwen25-7b.html
new file mode 100644
index 0000000..f5b3f8e
--- /dev/null
+++ b/data/artifacts/5dbb1c6732ae/qwen25-7b.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Star Clock</title>
+ <style>
+ body { margin: 0; overflow: hidden; background: black; }
+ .clock { position: absolute; width: 100%; height: 100%; }
+ #time { position: absolute; bottom: 2rem; left: 2rem; color: white; font-size: 24px; }
+ </style>
+</head>
+<body>
+<div id="clock" class="clock"></div>
+<script>
+ const clock = document.getElementById('clock');
+ const timeDisplay = document.createElement('span');
+ timeDisplay.id = 'time';
+ clock.appendChild(timeDisplay);
+
+ function drawStar(x, y) {
+ const star = document.createElementNS("http://www.w3.org/2000/svg", "circle");
+ star.setAttribute("cx", x);
+ star.setAttribute("cy", y);
+ star.setAttribute("r", 1);
+ star.style.fillOpacity = Math.random() * 0.8;
+ clock.appendChild(star);
+ }
+
+ function drawHand(hand) {
+ const handLength = 200;
+ const angle = (hand / 60) * Math.PI * 2 - Math.PI / 2;
+ const x = handLength * Math.cos(angle) + window.innerWidth / 2;
+ const y = handLength * Math.sin(-angle) + window.innerHeight / 2;
+ drawStar(x, y);
+ }
+
+ function updateClock() {
+ const now = new Date();
+ timeDisplay.textContent = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
+
+ drawHand(now.getHours() * 30);
+ drawHand(now.getMinutes() * 6 + now.getSeconds() / 10);
+ drawHand(now.getSeconds() * 6);
+
+ const shootingStar = document.createElementNS("http://www.w3.org/2000/svg", "line");
+ shootingStar.setAttribute("x1", Math.random() * window.innerWidth);
+ shootingStar.setAttribute("y1", window.innerHeight);
+ shootingStar.setAttribute("x2", Math.random() * window.innerWidth);
+ shootingStar.setAttribute("y2", 0);
+ shootingStar.style.stroke = "#fff";
+ shootingStar.style.strokeWidth = "2";
+ clock.appendChild(shootingStar);
+
+ setTimeout(updateClock, 1000 - now.getMilliseconds());
+ }
+
+ updateClock();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/5dbb1c6732ae/qwen25-7b.png b/data/artifacts/5dbb1c6732ae/qwen25-7b.png
new file mode 100644
index 0000000..ee9e619
Binary files /dev/null and b/data/artifacts/5dbb1c6732ae/qwen25-7b.png differ
diff --git a/data/artifacts/5dbb1c6732ae/qwen3-14b.html b/data/artifacts/5dbb1c6732ae/qwen3-14b.html
new file mode 100644
index 0000000..2802e77
--- /dev/null
+++ b/data/artifacts/5dbb1c6732ae/qwen3-14b.html
@@ -0,0 +1,175 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Star Clock</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: black;
+ height: 100%;
+ width: 100%;
+ font-family: 'Courier New', Courier, monospace;
+ color: white;
+ }
+ canvas {
+ display: block;
+ }
+ #timeDisplay {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ font-size: 24px;
+ text-shadow: 0 0 5px black;
+ }
+ </style>
+</head>
+<body>
+ <div id="timeDisplay">00:00:00</div>
+ <canvas id="starCanvas"></canvas>
+ <script>
+ const canvas = document.getElementById('starCanvas');
+ const ctx = canvas.getContext('2d');
+ const timeDisplay = document.getElementById('timeDisplay');
+
+ let width = canvas.width = window.innerWidth;
+ let height = canvas.height = window.innerHeight;
+
+ window.addEventListener('resize', () => {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ });
+
+ const stars = [];
+ const constellations = [
+ { name: 'hour', points: 4, color: '#00ffff', speed: 0.0005 },
+ { name: 'minute', points: 6, color: '#ff00ff', speed: 0.001 },
+ { name: 'second', points: 8, color: '#ffff00', speed: 0.002 }
+ ];
+
+ const starCount = 500;
+ const maxStarSize = 2;
+ const minStarSize = 0.5;
+
+ const shootingStarInterval = 10000;
+ const shootingStarDuration = 2000;
+ let lastShootingStarTime = 0;
+
+ function random(min, max) {
+ return Math.random() * (max - min) + min;
+ }
+
+ function createStars() {
+ stars.length = 0;
+ for (let i = 0; i < starCount; i++) {
+ stars.push({
+ x: random(0, width),
+ y: random(0, height),
+ size: random(minStarSize, maxStarSize),
+ twinkle: random(0.5, 1.5),
+ twinkleSpeed: random(0.001, 0.005)
+ });
+ }
+ }
+
+ function drawStars() {
+ ctx.fillStyle = 'black';
+ ctx.fillRect(0, 0, width, height);
+
+ for (let star of stars) {
+ star.size = (Math.sin(Date.now() * star.twinkleSpeed) * star.twinkle + 1) * maxStarSize;
+ ctx.beginPath();
+ ctx.arc(star.x, star.y, star.size, 0, Math.PI * 2);
+ ctx.fillStyle = 'white';
+ ctx.fill();
+ }
+ }
+
+ function drawConstellations() {
+ const now = new Date();
+ const hours = now.getHours() % 12;
+ const minutes = now.getMinutes();
+ const seconds = now.getSeconds();
+
+ for (let constellation of constellations) {
+ const points = constellation.points;
+ const angleStep = (2 * Math.PI) / points;
+ const constellationAngle = (constellation.name === 'hour' ? hours : constellation.name === 'minute' ? minutes : seconds) * (Math.PI / 30);
+ const constellationRadius = (constellation.name === 'hour' ? 100 : constellation.name === 'minute' ? 150 : 200);
+
+ ctx.beginPath();
+ ctx.strokeStyle = constellation.color;
+ ctx.lineWidth = 1.5;
+
+ for (let i = 0; i < points; i++) {
+ const angle = constellationAngle + i * angleStep;
+ const x = width / 2 + Math.cos(angle) * constellationRadius;
+ const y = height / 2 + Math.sin(angle) * constellationRadius;
+ if (i === 0) {
+ ctx.moveTo(x, y);
+ } else {
+ ctx.lineTo(x, y);
+ }
+ }
+
+ ctx.stroke();
+ }
+ }
+
+ function drawTime() {
+ const now = new Date();
+ const hours = now.getHours().toString().padStart(2, '0');
+ const minutes = now.getMinutes().toString().padStart(2, '0');
+ const seconds = now.getSeconds().toString().padStart(2, '0');
+ timeDisplay.textContent = `${hours}:${minutes}:${seconds}`;
+ }
+
+ function drawShootingStar() {
+ const now = Date.now();
+ if (now - lastShootingStarTime < shootingStarInterval) return;
+
+ const startX = random(0, width);
+ const startY = random(0, height);
+ const endX = random(0, width);
+ const endY = random(0, height);
+ const length = Math.sqrt((endX - startX) ** 2 + (endY - startY) ** 2);
+ const duration = shootingStarDuration;
+ const startTime = now;
+
+ const draw = () => {
+ const elapsed = Date.now() - startTime;
+ const progress = Math.min(elapsed / duration, 1);
+ const x = startX + (endX - startX) * progress;
+ const y = startY + (endY - startY) * progress;
+ const fade = 1 - progress;
+ ctx.beginPath();
+ ctx.strokeStyle = 'white';
+ ctx.lineWidth = 2 + 10 * (1 - fade);
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + (endX - startX) * 0.1, y + (endY - startY) * 0.1);
+ ctx.stroke();
+ if (progress < 1) {
+ requestAnimationFrame(draw);
+ }
+ };
+
+ draw();
+ lastShootingStarTime = now;
+ }
+
+ function animate() {
+ drawStars();
+ drawConstellations();
+ drawTime();
+ drawShootingStar();
+ requestAnimationFrame(animate);
+ }
+
+ createStars();
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/5dbb1c6732ae/qwen3-14b.png b/data/artifacts/5dbb1c6732ae/qwen3-14b.png
new file mode 100644
index 0000000..670f2b8
Binary files /dev/null and b/data/artifacts/5dbb1c6732ae/qwen3-14b.png differ
diff --git a/data/artifacts/68b40458a368/hermes3-8b.html b/data/artifacts/68b40458a368/hermes3-8b.html
new file mode 100644
index 0000000..4ae6a8b
--- /dev/null
+++ b/data/artifacts/68b40458a368/hermes3-8b.html
@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Rope Physics Toy</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ }
+ canvas {
+ display: block;
+ }
+ #control-panel {
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ background-color: rgba(255, 255, 255, 0.7);
+ padding: 5px;
+ border-radius: 5px;
+ }
+ #control-panel button {
+ margin-right: 5px;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="canvas"></canvas>
+ <div id="control-panel">
+ <button id="reset">Reset</button>
+ <button id="cut-tool">Cut Tool</button>
+ </div>
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+
+ let width, height;
+ let points = [];
+ let newPoints = [];
+ let indexToMove = null;
+
+ function init() {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight - 40; // Leave space for controls
+ createRope();
+ }
+
+ function createRope(numPoints = 50) {
+ points = [];
+ newPoints = [];
+
+ const angle = Math.random() * Math.PI * 2;
+ const startX = Math.cos(angle) * 100;
+ const startZ = Math.sin(angle) * 100;
+
+ for (let i = 0; i < numPoints; i++) {
+ let angle = Math.random() * Math.PI * 2;
+ let x = Math.cos(angle) * 50 + startX;
+ let z = Math.sin(angle) * 50 + startZ;
+ points.push({x, y: 0, z});
+ }
+
+ updatePoints();
+ }
+
+ function updatePoints() {
+ newPoints = [];
+
+ for (let i = 0; i < points.length; i++) {
+ const p1 = points[i];
+ const p2 = i === points.length - 1 ? points[0] : points[i + 1];
+
+ const dx = p2.x - p1.x;
+ const dz = p2.z - p1.z;
+
+ const angle = Math.atan2(dz, dx);
+ const length = Math.hypot(dx, dz);
+
+ for (let j = 0; j <= 1 || i === 0; j++) {
+ let x = p1.x + Math.cos(angle) * (j * length);
+ let z = p1.z + Math.sin(angle) * (j * length);
+ newPoints.push({x, y: p1.y, z});
+ }
+ }
+
+ points = newPoints;
+ draw();
+ }
+
+ function draw() {
+ ctx.clearRect(0, 0, width, height);
+
+ for (let i = 0; i < points.length; i++) {
+ const p = points[i];
+ ctx.beginPath();
+ ctx.moveTo(p.x, p.y);
+ ctx.lineTo(p.x, p.y + 50);
+ ctx.strokeStyle = 'black';
+ ctx.stroke();
+
+ if (indexToMove === i) continue;
+
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, 3, 0, Math.PI * 2);
+ ctx.fillStyle = 'blue';
+ ctx.fill();
+ }
+ }
+
+ canvas.addEventListener('mousemove', (e) => {
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ indexToMove = null;
+ for (let i = 0; i < points.length; i++) {
+ const p = points[i];
+ if (Math.abs(p.x - x) <= 3 && Math.abs(p.y - y) <= 3) {
+ indexToMove = i;
+ break;
+ }
+ }
+ });
+
+ canvas.addEventListener('click', (e) => {
+ if (indexToMove !== null) {
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ points[indexToMove].x = x;
+ points[indexToMove].y = y;
+
+ updatePoints();
+ }
+ });
+
+ document.getElementById('reset').addEventListener('click', createRope);
+
+ function cut(points, index) {
+ points.splice(index, 1);
+ newPoints.pop(); // Remove the last point from newPoints
+ points.push({ ...points[0], z: newPoints[0].z });
+ updatePoints();
+ }
+
+ document.getElementById('cut-tool').addEventListener('click', () => {
+ if (indexToMove === null) return;
+
+ const index = Math.floor(indexToMove / 2);
+ cut(points, index);
+ });
+
+ init();
+
+ setInterval(() => {
+ for (let i = 0; i < points.length; i++) {
+ points[i].y += 0.5;
+ }
+ updatePoints();
+ }, 10);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/68b40458a368/hf-qwen-coder-32b.html b/data/artifacts/68b40458a368/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..667f2ac
--- /dev/null
+++ b/data/artifacts/68b40458a368/hf-qwen-coder-32b.html
@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Verlet Rope Physics</title>
+<style>
+ body { margin: 0; overflow: hidden; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f0f0; }
+ canvas { border: 1px solid #ccc; cursor: grab; }
+ button { position: absolute; top: 10px; right: 10px; padding: 10px 20px; font-size: 16px; cursor: pointer; }
+</style>
+</head>
+<body>
+<canvas id="physicsCanvas"></canvas>
+<button onclick="reset()">Reset</button>
+<script>
+const canvas = document.getElementById('physicsCanvas');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+let points = [];
+let constraints = [];
+const numPoints = 15;
+const distance = 30;
+const gravity = 0.1;
+const friction = 0.99;
+let mouse = { x: 0, y: 0, down: false };
+
+function Point(x, y) {
+ this.x = x;
+ this.y = y;
+ this.oldX = x;
+ this.oldY = y;
+}
+
+Point.prototype.update = function() {
+ const vx = (this.x - this.oldX) * friction;
+ const vy = (this.y - this.oldY) * friction;
+
+ this.oldX = this.x;
+ this.oldY = this.y;
+
+ this.x += vx;
+ this.y += vy;
+
+ if (mouse.down && Math.hypot(this.x - mouse.x, this.y - mouse.y) < distance / 2) {
+ this.x = mouse.x;
+ this.y = mouse.y;
+ } else {
+ this.y += gravity;
+ }
+};
+
+Point.prototype.draw = function() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, 4, 0, Math.PI * 2);
+ ctx.fill();
+};
+
+function Constraint(p1, p2) {
+ this.p1 = p1;
+ this.p2 = p2;
+}
+
+Constraint.prototype.update = function() {
+ const dx = this.p1.x - this.p2.x;
+ const dy = this.p1.y - this.p2.y;
+ let dist = Math.sqrt(dx * dx + dy * dy);
+ const diff = (dist - distance) / dist;
+
+ const offsetX = dx * diff * 0.5;
+ const offsetY = dy * diff * 0.5;
+
+ if (!mouse.down || Math.hypot(this.p1.x - mouse.x, this.p1.y - mouse.y) > distance / 2) {
+ this.p1.x -= offsetX;
+ this.p1.y -= offsetY;
+ }
+ if (!mouse.down || Math.hypot(this.p2.x - mouse.x, this.p2.y - mouse.y) > distance / 2) {
+ this.p2.x += offsetX;
+ this.p2.y += offsetY;
+ }
+};
+
+Constraint.prototype.draw = function() {
+ ctx.beginPath();
+ ctx.moveTo(this.p1.x, this.p1.y);
+ ctx.lineTo(this.p2.x, this.p2.y);
+ ctx.stroke();
+};
+
+function cutSegment(x, y) {
+ for (let i = constraints.length - 1; i >= 0; i--) {
+ const constraint = constraints[i];
+ const p1 = constraint.p1;
+ const p2 = constraint.p2;
+ if (Math.hypot((x + p2.x - p1.x) * (p2.y - p1.y) - (y + p2.y - p1.y) * (p2.x - p1.x), 0) < distance / 4) {
+ constraints.splice(i, 1);
+ }
+ }
+}
+
+function reset() {
+ points = [];
+ constraints = [];
+ for (let i = 0; i < numPoints; i++) {
+ const point = new Point(canvas.width / 2 + i * distance, canvas.height / 4);
+ points.push(point);
+ if (i > 0) {
+ constraints.push(new Constraint(points[i - 1], points[i]));
+ }
+ }
+}
+
+reset();
+
+canvas.addEventListener('mousedown', function(e) {
+ mouse.down = true;
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+});
+
+canvas.addEventListener('mousemove', function(e) {
+ if (mouse.down) {
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+ }
+});
+
+canvas.addEventListener('mouseup', function() {
+ mouse.down = false;
+});
+
+canvas.addEventListener('contextmenu', function(e) {
+ e.preventDefault();
+ cutSegment(e.clientX, e.clientY);
+}, false);
+
+function update() {
+ for (let point of points) {
+ point.update();
+ }
+
+ for (let constraint of constraints) {
+ constraint.update();
+ }
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let constraint of constraints) {
+ constraint.draw();
+ }
+
+ for (let point of points) {
+ point.draw();
+ }
+
+ requestAnimationFrame(update);
+}
+
+update();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/68b40458a368/hf-qwen-coder-32b.png b/data/artifacts/68b40458a368/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..93b86ac
Binary files /dev/null and b/data/artifacts/68b40458a368/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/78dd0efa6a47/claude-code.html b/data/artifacts/78dd0efa6a47/claude-code.html
new file mode 100644
index 0000000..e7f245e
--- /dev/null
+++ b/data/artifacts/78dd0efa6a47/claude-code.html
@@ -0,0 +1,381 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Metaball Lava Lamp</title>
+<style>
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+ html, body { width: 100%; height: 100%; overflow: hidden; background: #000; }
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }
+ #lamp { display: block; width: 100vw; height: 100vh; }
+
+ #panel {
+ position: fixed;
+ top: 18px;
+ left: 18px;
+ padding: 16px 18px 18px;
+ border-radius: 16px;
+ background: rgba(20, 12, 30, 0.42);
+ backdrop-filter: blur(14px) saturate(140%);
+ -webkit-backdrop-filter: blur(14px) saturate(140%);
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.45);
+ color: #f4eef8;
+ width: 224px;
+ user-select: none;
+ z-index: 10;
+ transition: opacity .3s;
+ }
+ #panel h1 {
+ font-size: 13px;
+ font-weight: 600;
+ letter-spacing: 1.6px;
+ text-transform: uppercase;
+ opacity: 0.85;
+ margin-bottom: 14px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+ #panel h1 .dot {
+ width: 9px; height: 9px; border-radius: 50%;
+ background: radial-gradient(circle at 30% 30%, #ffe066, #ff2d55);
+ box-shadow: 0 0 10px #ff5e3a;
+ }
+ .row { margin-bottom: 14px; }
+ .row:last-child { margin-bottom: 0; }
+ label {
+ display: block;
+ font-size: 11px;
+ letter-spacing: 0.5px;
+ opacity: 0.7;
+ margin-bottom: 7px;
+ display: flex;
+ justify-content: space-between;
+ }
+ label b { opacity: 0.95; font-weight: 600; }
+ select {
+ width: 100%;
+ padding: 8px 10px;
+ border-radius: 9px;
+ background: rgba(255,255,255,0.07);
+ color: #f4eef8;
+ border: 1px solid rgba(255,255,255,0.14);
+ font-size: 13px;
+ outline: none;
+ cursor: pointer;
+ appearance: none;
+ -webkit-appearance: none;
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M2 4l4 4 4-4' stroke='%23f4eef8' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
+ background-repeat: no-repeat;
+ background-position: right 10px center;
+ }
+ select option { background: #1a1024; color: #f4eef8; }
+
+ input[type=range] {
+ width: 100%;
+ -webkit-appearance: none;
+ appearance: none;
+ height: 5px;
+ border-radius: 5px;
+ background: rgba(255,255,255,0.18);
+ outline: none;
+ cursor: pointer;
+ }
+ input[type=range]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ width: 17px; height: 17px;
+ border-radius: 50%;
+ background: radial-gradient(circle at 32% 30%, #ffe9a8, #ff7a00);
+ border: 2px solid rgba(255,255,255,0.5);
+ box-shadow: 0 0 8px rgba(255,120,40,0.7);
+ }
+ input[type=range]::-moz-range-thumb {
+ width: 15px; height: 15px;
+ border-radius: 50%;
+ background: radial-gradient(circle at 32% 30%, #ffe9a8, #ff7a00);
+ border: 2px solid rgba(255,255,255,0.5);
+ box-shadow: 0 0 8px rgba(255,120,40,0.7);
+ }
+ #hint {
+ position: fixed;
+ bottom: 14px;
+ left: 0; right: 0;
+ text-align: center;
+ color: rgba(255,255,255,0.28);
+ font-size: 11px;
+ letter-spacing: 1px;
+ z-index: 5;
+ pointer-events: none;
+ }
+</style>
+</head>
+<body>
+<canvas id="lamp"></canvas>
+
+<div id="panel">
+ <h1><span class="dot"></span>Lava Lamp</h1>
+ <div class="row">
+ <label>Color Theme</label>
+ <select id="theme">
+ <option value="classic">Classic Lava</option>
+ <option value="ember">Ember Glow</option>
+ <option value="sunset">Sunset</option>
+ <option value="amethyst">Amethyst</option>
+ <option value="ocean">Deep Ocean</option>
+ <option value="toxic">Toxic</option>
+ </select>
+ </div>
+ <div class="row">
+ <label>Blobs <b id="countVal">12</b></label>
+ <input type="range" id="count" min="3" max="26" value="12">
+ </div>
+</div>
+
+<div id="hint">metaballs rise · fall · merge</div>
+
+<script>
+(function () {
+ "use strict";
+
+ const canvas = document.getElementById("lamp");
+ const ctx = canvas.getContext("2d");
+
+ // Offscreen low-res buffer for the metaball field
+ const off = document.createElement("canvas");
+ const offCtx = off.getContext("2d");
+
+ // ---- Themes ----------------------------------------------------------
+ // bg: vertical background gradient [top, bottom]
+ // stops: blob color ramp (edge -> core)
+ const THEMES = {
+ classic: {
+ bg: ["#2a0a3a", "#160326"],
+ stops: [[0,"#7a0f3a"],[0.35,"#ff2d55"],[0.7,"#ff8a00"],[1,"#ffe066"]]
+ },
+ ember: {
+ bg: ["#220a05", "#120302"],
+ stops: [[0,"#7a1500"],[0.4,"#ff3d00"],[0.72,"#ff7a00"],[1,"#ffc46b"]]
+ },
+ sunset: {
+ bg: ["#1b1035", "#360d38"],
+ stops: [[0,"#5b1250"],[0.35,"#ff4d6d"],[0.7,"#ff9e5e"],[1,"#ffd76e"]]
+ },
+ amethyst: {
+ bg: ["#14052a", "#26063f"],
+ stops: [[0,"#5a1a8a"],[0.4,"#c04dff"],[0.72,"#ff5edb"],[1,"#ffd1f5"]]
+ },
+ ocean: {
+ bg: ["#041b2d", "#06263a"],
+ stops: [[0,"#053a5c"],[0.38,"#00c8ff"],[0.72,"#2b8cff"],[1,"#bfeaff"]]
+ },
+ toxic: {
+ bg: ["#071a07", "#0a250a"],
+ stops: [[0,"#2c5c00"],[0.4,"#7bd400"],[0.72,"#39ff14"],[1,"#eaffb0"]]
+ }
+ };
+
+ let theme = THEMES.classic;
+ let ramp = new Uint8Array(256 * 3); // color lookup: edge->core
+
+ function hexToRgb(h) {
+ h = h.replace("#", "");
+ return [parseInt(h.slice(0,2),16), parseInt(h.slice(2,4),16), parseInt(h.slice(4,6),16)];
+ }
+
+ function buildRamp(t) {
+ const stops = t.stops;
+ for (let i = 0; i < 256; i++) {
+ const p = i / 255;
+ let a = stops[0], b = stops[stops.length - 1];
+ for (let s = 0; s < stops.length - 1; s++) {
+ if (p >= stops[s][0] && p <= stops[s + 1][0]) { a = stops[s]; b = stops[s + 1]; break; }
+ }
+ const span = (b[0] - a[0]) || 1;
+ const f = (p - a[0]) / span;
+ const ca = hexToRgb(a[1]), cb = hexToRgb(b[1]);
+ ramp[i*3] = ca[0] + (cb[0] - ca[0]) * f;
+ ramp[i*3+1] = ca[1] + (cb[1] - ca[1]) * f;
+ ramp[i*3+2] = ca[2] + (cb[2] - ca[2]) * f;
+ }
+ }
+ buildRamp(theme);
+
+ // ---- Sizing ----------------------------------------------------------
+ let W = 0, H = 0, minDim = 0;
+ let lowW = 0, lowH = 0, scale = 0.22;
+ let imgData = null, buf = null;
+ let bgGrad = null;
+
+ function resize() {
+ W = canvas.width = Math.max(1, window.innerWidth);
+ H = canvas.height = Math.max(1, window.innerHeight);
+ minDim = Math.min(W, H);
+
+ // Keep the low-res field around ~200px on the long edge
+ scale = Math.min(0.26, 200 / Math.max(W, H));
+ lowW = Math.max(2, Math.round(W * scale));
+ lowH = Math.max(2, Math.round(H * scale));
+ off.width = lowW;
+ off.height = lowH;
+ imgData = offCtx.createImageData(lowW, lowH);
+ buf = imgData.data;
+
+ bgGrad = ctx.createLinearGradient(0, 0, 0, H);
+ bgGrad.addColorStop(0, theme.bg[0]);
+ bgGrad.addColorStop(1, theme.bg[1]);
+
+ for (const b of blobs) recomputeRadius(b);
+ }
+
+ // ---- Blobs -----------------------------------------------------------
+ const blobs = [];
+
+ function recomputeRadius(b) {
+ b.r = b.rFrac * minDim;
+ }
+
+ function makeBlob() {
+ const b = {
+ x: Math.random() * (W || 800),
+ y: Math.random() * (H || 600),
+ rFrac: 0.05 + Math.random() * 0.055,
+ vs: 0.25 + Math.random() * 0.5, // vertical osc speed
+ vp: Math.random() * Math.PI * 2, // vertical phase
+ vamp: 45 + Math.random() * 70, // vertical amplitude (px/s)
+ hs: 0.15 + Math.random() * 0.35, // horizontal speed
+ hp: Math.random() * Math.PI * 2,
+ hamp: 18 + Math.random() * 34, // horizontal drift
+ ps: 0.4 + Math.random() * 0.8, // pulse speed
+ pp: Math.random() * Math.PI * 2,
+ r: 40
+ };
+ recomputeRadius(b);
+ return b;
+ }
+
+ function setCount(n) {
+ while (blobs.length < n) blobs.push(makeBlob());
+ while (blobs.length > n) blobs.pop();
+ }
+
+ // ---- Physics ---------------------------------------------------------
+ function update(t, dt) {
+ for (const b of blobs) {
+ const vy = Math.sin(t * b.vs + b.vp) * b.vamp;
+ const vx = Math.sin(t * b.hs + b.hp) * b.hamp;
+ b.x += vx * dt;
+ b.y += vy * dt;
+
+ const pad = b.r * 0.35;
+ if (b.x < pad) { b.x = pad; b.hp += Math.PI; }
+ else if (b.x > W - pad) { b.x = W - pad; b.hp += Math.PI; }
+ if (b.y < pad) { b.y = pad; b.vp += Math.PI; }
+ else if (b.y > H - pad) { b.y = H - pad; b.vp += Math.PI; }
+
+ b.rr = b.r * (1 + Math.sin(t * b.ps + b.pp) * 0.14); // pulsating radius
+ }
+ }
+
+ // ---- Metaball render -------------------------------------------------
+ function renderField(t) {
+ // Precompute low-res blob params
+ const n = blobs.length;
+ const bx = new Float32Array(n), by = new Float32Array(n), br2 = new Float32Array(n);
+ for (let i = 0; i < n; i++) {
+ const b = blobs[i];
+ bx[i] = b.x * scale;
+ by[i] = b.y * scale;
+ const rl = b.rr * scale;
+ br2[i] = rl * rl;
+ }
+
+ let p = 0;
+ for (let y = 0; y < lowH; y++) {
+ for (let x = 0; x < lowW; x++) {
+ let sum = 0;
+ for (let i = 0; i < n; i++) {
+ const dx = x - bx[i], dy = y - by[i];
+ const d2 = dx * dx + dy * dy + 0.0001;
+ sum += br2[i] / d2;
+ }
+ let a;
+ if (sum < 0.65) {
+ a = 0;
+ } else {
+ a = sum < 1.05 ? (sum - 0.65) / 0.4 : 1;
+ let ct = (sum - 0.7) / 2.6; // color param edge->core
+ if (ct < 0) ct = 0; else if (ct > 1) ct = 1;
+ const ci = (ct * 255) | 0;
+ const r3 = ci * 3;
+ buf[p] = ramp[r3];
+ buf[p + 1] = ramp[r3 + 1];
+ buf[p + 2] = ramp[r3 + 2];
+ }
+ buf[p + 3] = a * 255;
+ p += 4;
+ }
+ }
+ offCtx.putImageData(imgData, 0, 0);
+ }
+
+ // ---- Main loop -------------------------------------------------------
+ let last = performance.now();
+ function frame(now) {
+ let dt = (now - last) / 1000;
+ if (dt > 0.05) dt = 0.05; // clamp after tab switch
+ last = now;
+ const t = now / 1000;
+
+ update(t, dt);
+ renderField(t);
+
+ // Background gradient
+ ctx.globalCompositeOperation = "source-over";
+ ctx.fillStyle = bgGrad;
+ ctx.fillRect(0, 0, W, H);
+
+ // Upscale the metaball layer smoothly on top
+ ctx.imageSmoothingEnabled = true;
+ ctx.imageSmoothingQuality = "high";
+ ctx.drawImage(off, 0, 0, lowW, lowH, 0, 0, W, H);
+
+ // Soft glow pass
+ ctx.globalCompositeOperation = "lighter";
+ ctx.globalAlpha = 0.28;
+ ctx.drawImage(off, 0, 0, lowW, lowH, 0, 0, W, H);
+ ctx.globalAlpha = 1;
+ ctx.globalCompositeOperation = "source-over";
+
+ requestAnimationFrame(frame);
+ }
+
+ // ---- Controls --------------------------------------------------------
+ const themeSel = document.getElementById("theme");
+ const countInp = document.getElementById("count");
+ const countVal = document.getElementById("countVal");
+
+ themeSel.addEventListener("change", function () {
+ theme = THEMES[themeSel.value] || THEMES.classic;
+ buildRamp(theme);
+ bgGrad = ctx.createLinearGradient(0, 0, 0, H);
+ bgGrad.addColorStop(0, theme.bg[0]);
+ bgGrad.addColorStop(1, theme.bg[1]);
+ });
+
+ countInp.addEventListener("input", function () {
+ countVal.textContent = countInp.value;
+ setCount(parseInt(countInp.value, 10));
+ });
+
+ window.addEventListener("resize", resize);
+
+ // ---- Boot ------------------------------------------------------------
+ setCount(parseInt(countInp.value, 10));
+ resize();
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/78dd0efa6a47/claude-code.png b/data/artifacts/78dd0efa6a47/claude-code.png
new file mode 100644
index 0000000..3b5c190
Binary files /dev/null and b/data/artifacts/78dd0efa6a47/claude-code.png differ
diff --git a/data/artifacts/78dd0efa6a47/gemma3-12b.html b/data/artifacts/78dd0efa6a47/gemma3-12b.html
new file mode 100644
index 0000000..bcc7c74
--- /dev/null
+++ b/data/artifacts/78dd0efa6a47/gemma3-12b.html
@@ -0,0 +1,156 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Metaball Lava Lamp</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: #222; }
+ #controls { position: absolute; top: 10px; left: 10px; width: 100%; display: flex; gap: 10px; align-items: center; color: white; font-family: sans-serif; }
+ label { font-size: 14px; }
+ </style>
+</head>
+<body>
+ <div id="controls">
+ <label for="colorTheme">Color Theme:</label>
+ <select id="colorTheme" onchange="updateColors()">
+ <option value="warm">Warm</option>
+ <option value="cool">Cool</option>
+ <option value="retro">Retro</option>
+ </select>
+
+ <label for="blobCount">Blob Count:</label>
+ <input type="range" id="blobCount" min="5" max="30" value="15" onchange="updateBlobCount(this.value)">
+ </div>
+
+ <canvas id="lavaLampCanvas"></canvas>
+
+ <script>
+ const canvas = document.getElementById('lavaLampCanvas');
+ const ctx = canvas.getContext('2d');
+ let blobs = [];
+ let colorTheme = 'warm';
+ let blobCount = 15;
+
+
+ function init() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ for (let i = 0; i < blobCount; i++) {
+ blobs.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ radius: Math.random() * 15 + 5,
+ speedX: (Math.random() - 0.5) * 0.5,
+ speedY: (Math.random() - 0.5) * 0.5,
+ color: getRandomColor()
+ });
+ }
+
+ animate();
+ }
+
+
+ function updateColors() {
+ colorTheme = document.getElementById('colorTheme').value;
+ blobs.forEach(blob => blob.color = getRandomColor());
+ }
+
+ function updateBlobCount(count) {
+ blobCount = parseInt(count);
+ blobs = []; // Clear existing blobs
+ for (let i = 0; i < blobCount; i++) {
+ blobs.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ radius: Math.random() * 15 + 5,
+ speedX: (Math.random() - 0.5) * 0.5,
+ speedY: (Math.random() - 0.5) * 0.5,
+ color: getRandomColor()
+ });
+ }
+ }
+
+
+ function getRandomColor() {
+ switch (colorTheme) {
+ case 'warm':
+ return `rgba(${255 + Math.random() * -50}, ${150 + Math.random() * 50}, ${50 + Math.random() * 50}, 0.8)`;
+ case 'cool':
+ return `rgba(${50 + Math.random() * 50}, ${150 + Math.random() * 50}, ${255 + Math.random() * -50}, 0.8)`;
+ case 'retro':
+ return `rgba(${255 - Math.random()*75}, ${Math.random()*150+30}, ${50 + Math.random()*50}, 0.8)`;
+
+ default:
+ return `rgba(255, 255, 255, 0.8)`;
+ }
+ }
+
+
+ function draw() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = 0; i < blobs.length; i++) {
+ const blob = blobs[i];
+
+ blob.x += blob.speedX;
+ blob.y += blob.speedY;
+
+ // Bounce off walls
+ if (blob.x + blob.radius > canvas.width || blob.x - blob.radius < 0) {
+ blob.speedX = -blob.speedX;
+ }
+ if (blob.y + blob.radius > canvas.height || blob.y - blob.radius < 0) {
+ blob.speedY = -blob.speedY;
+ }
+
+ ctx.beginPath();
+ ctx.arc(blob.x, blob.y, blob.radius, 0, Math.PI * 2);
+ ctx.fillStyle = blob.color;
+ ctx.fill();
+ }
+
+
+ // Metaball merging (very basic)
+ for (let i = 0; i < blobs.length; i++) {
+ for (let j = i + 1; j < blobs.length; j++) {
+ const blob1 = blobs[i];
+ const blob2 = blobs[j];
+
+ const dx = blob1.x - blob2.x;
+ const dy = blob1.y - blob2.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance < blob1.radius + blob2.radius) {
+ // Simple merging: reduce radius of both blobs slightly (more complex blending could be added here).
+ blob1.radius -= 0.1;
+ blob2.radius -= 0.1;
+ if(blob1.radius < 0) blob1.radius = 0;
+ if(blob2.radius < 0) blob2.radius = 0;
+ }
+
+ }
+ }
+
+
+ }
+
+ function animate() {
+ draw();
+ requestAnimationFrame(animate);
+ }
+
+ window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ blobs.forEach(blob => {
+ blob.x = Math.random() * canvas.width;
+ blob.y = Math.random() * canvas.height;
+ });
+ });
+
+ init();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/78dd0efa6a47/gemma3-12b.png b/data/artifacts/78dd0efa6a47/gemma3-12b.png
new file mode 100644
index 0000000..9d5a358
Binary files /dev/null and b/data/artifacts/78dd0efa6a47/gemma3-12b.png differ
diff --git a/data/artifacts/78dd0efa6a47/hermes3-8b.html b/data/artifacts/78dd0efa6a47/hermes3-8b.html
new file mode 100644
index 0000000..be637b2
--- /dev/null
+++ b/data/artifacts/78dd0efa6a47/hermes3-8b.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Metaball Lava Lamp</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ background-color: #000;
+ overflow: hidden;
+ }
+
+ canvas {
+ display: block;
+ background: #222;
+ }
+
+ #controls {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ label {
+ font-size: 18px;
+ color: #fff;
+ }
+
+ input[type="range"] {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ width: 100%;
+ margin-top: 5px;
+ }
+
+ input[type="color"] {
+ padding: 5px;
+ cursor: color;
+ }
+ </style>
+</head>
+<body>
+ <div id="controls">
+ <label for="blobCount">Blob Count:</label>
+ <input type="range" id="blobCount" min="1" max="20" value="10">
+
+ <label for="colorPicker">Color Theme:</label>
+ <input type="color" id="colorPicker" value="#FF4500">
+ </div>
+
+ <canvas id="metaball"></canvas>
+
+ <script>
+ const canvas = document.getElementById('metaball');
+ const ctx = canvas.getContext('2d');
+
+ let blobCount = 10;
+ let colorTheme = '#FF4500';
+ let blobs = [];
+
+ function Metaball() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ for (let i = 0; i < blobCount; i++) {
+ blobs.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ radius: Math.random() * 10 + 5,
+ color: colorTheme
+ });
+ }
+ }
+
+ function render() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = 0; i < blobs.length; i++) {
+ const blob = blobs[i];
+
+ ctx.beginPath();
+ ctx.arc(blob.x, blob.y, blob.radius, 0, 2 * Math.PI);
+ ctx.fillStyle = blob.color;
+ ctx.fill();
+ }
+ }
+
+ function update() {
+ for (let i = 0; i < blobs.length; i++) {
+ const blob = blobs[i];
+
+ blob.x += (Math.random() - 0.5) * 2;
+ blob.y += (Math.random() - 0.5) * 2;
+
+ if (blob.x + blob.radius > canvas.width || blob.x - blob.radius < 0 ||
+ blob.y + blob.radius > canvas.height || blob.y - blob.radius < 0) {
+ blobs.splice(i, 1);
+ i--;
+
+ const newBlob = {
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ radius: Math.random() * 10 + 5,
+ color: colorTheme
+ };
+
+ blobs.splice(i, 0, newBlob);
+ }
+ }
+ }
+
+ Metaball();
+ setInterval(() => {
+ render();
+ update();
+ }, 20);
+
+ document.getElementById('blobCount').addEventListener('input', e => {
+ blobCount = parseInt(e.target.value);
+ });
+
+ document.getElementById('colorPicker').addEventListener('input', e => {
+ colorTheme = e.target.value;
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/78dd0efa6a47/hermes3-8b.png b/data/artifacts/78dd0efa6a47/hermes3-8b.png
new file mode 100644
index 0000000..99054a3
Binary files /dev/null and b/data/artifacts/78dd0efa6a47/hermes3-8b.png differ
diff --git a/data/artifacts/78dd0efa6a47/qwen25-7b.html b/data/artifacts/78dd0efa6a47/qwen25-7b.html
new file mode 100644
index 0000000..b08e619
--- /dev/null
+++ b/data/artifacts/78dd0efa6a47/qwen25-7b.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Metaball Lava Lamp</title>
+ <style>
+ body { margin: 0; overflow: hidden; display: flex; justify-content: center; align-items: center; height: 100vh; background: linear-gradient(to bottom, #f9e79f, #fff8c2); }
+ canvas { border: 1px solid #000; }
+ </style>
+</head>
+<body>
+ <div style="position:absolute; top:5px; left:5px;">
+ <label for="color-theme">Color Theme:</label>
+ <input type="range" id="color-theme" min="0" max="100" value="50">
+ <br><br>
+ <label for="blob-count">Blob Count:</label>
+ <input type="range" id="blob-count" min="2" max="10" value="4">
+ </div>
+ <canvas id="lava-canvas"></canvas>
+ <script>
+ const canvas = document.getElementById('lava-canvas');
+ const ctx = canvas.getContext('2d');
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ let blobs = [];
+ let colorTheme = 50;
+ const blobCount = 4;
+
+ function randomVec3() {
+ return [Math.random(), Math.random(), Math.random()];
+ }
+
+ function metaball(p1, p2) {
+ return 1 / (p1[0] - p2[0])**2 + 1 / (p1[1] - p2[1])**2;
+ }
+
+ function updateBlobs() {
+ for (let i = 0; i < blobs.length; i++) {
+ const b1 = blobs[i];
+ if (b1.y > canvas.height) {
+ b1.x = Math.random() * canvas.width;
+ b1.y = -50;
+ continue;
+ }
+ for (const b2 of blobs) {
+ if (b1 !== b2) {
+ const m = metaball([b1.x, b1.y], [b2.x, b2.y]);
+ b1.x += 0.1 * (b2.x - b1.x) / Math.sqrt(m);
+ b1.y += 0.1 * (b2.y - b1.y) / Math.sqrt(m);
+ }
+ }
+ b1.y += 0.3;
+ }
+ }
+
+ function drawBlobs() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
+ gradient.addColorStop(0, `hsl(${colorTheme + 60}, 100%, 50%)`);
+ gradient.addColorStop(1, `hsl(${colorTheme}, 100%, 50%)`);
+ ctx.fillStyle = gradient;
+ for (const b of blobs) {
+ const size = Math.sqrt(b.x**2 + b.y**2);
+ if (size < canvas.height / 3) ctx.beginPath();
+ else ctx.arc(b.x, b.y, size * 1.5, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ function initBlobs(count) {
+ for (let i = 0; i < count; i++) {
+ blobs.push({ x: Math.random() * canvas.width, y: -100 });
+ }
+ }
+
+ const updateInterval = setInterval(() => {
+ colorTheme = parseInt(document.getElementById('color-theme').value);
+ blobCount = document.getElementById('blob-count').value;
+ if (blobs.length < blobCount) initBlobs(blobCount - blobs.length);
+ updateBlobs();
+ drawBlobs();
+ }, 10);
+
+ window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ initBlobs(blobCount);
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/78dd0efa6a47/qwen25-7b.png b/data/artifacts/78dd0efa6a47/qwen25-7b.png
new file mode 100644
index 0000000..e01cb28
Binary files /dev/null and b/data/artifacts/78dd0efa6a47/qwen25-7b.png differ
diff --git a/data/artifacts/78dd0efa6a47/qwen3-14b.html b/data/artifacts/78dd0efa6a47/qwen3-14b.html
new file mode 100644
index 0000000..a0f243a
--- /dev/null
+++ b/data/artifacts/78dd0efa6a47/qwen3-14b.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Metaball Lava Lamp</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: 8px;
+ }
+ #controls label {
+ display: block;
+ margin: 5px 0 2px;
+ }
+ #controls input[type="range"] {
+ width: 200px;
+ }
+ #controls select {
+ width: 150px;
+ }
+ </style>
+</head>
+<body>
+ <div id="controls">
+ <label>Color Theme:
+ <select id="themeSelect">
+ <option value="warm">Warm</option>
+ <option value="cool">Cool</option>
+ <option value="neon">Neon</option>
+ </select>
+ </label>
+ <label>Blob Count:
+ <input type="range" id="blobCount" min="2" max="16" value="6">
+ </label>
+ </div>
+ <canvas id="lavaLamp"></canvas>
+ <script>
+ const canvas = document.getElementById('lavaLamp');
+ const ctx = canvas.getContext('2d');
+ let width, height;
+
+ function resize() {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ }
+ window.addEventListener('resize', resize);
+ resize();
+
+ const colors = {
+ warm: ['#FF6B6B', '#F8B33D', '#F9C74F', '#FFEEAD', '#D4A574'],
+ cool: ['#6C919C', '#5E6C84', '#3C5C7D', '#2F4F75', '#2C3E50'],
+ neon: ['#FF0066', '#00FF66', '#6600FF', '#FFFF00', '#00FFFF']
+ };
+
+ const defaultColorTheme = 'warm';
+ const themeSelect = document.getElementById('themeSelect');
+ const blobCountSlider = document.getElementById('blobCount');
+
+ let blobCount = parseInt(blobCountSlider.value);
+ blobCountSlider.addEventListener('input', () => {
+ blobCount = parseInt(blobCountSlider.value);
+ updateBlobs();
+ });
+
+ let colorTheme = defaultColorTheme;
+ themeSelect.addEventListener('change', (e) => {
+ colorTheme = e.target.value;
+ updateBlobs();
+ });
+
+ function updateBlobs() {
+ blobs = [];
+ for (let i = 0; i < blobCount; i++) {
+ blobs.push({
+ x: Math.random() * width,
+ y: Math.random() * height,
+ radius: Math.random() * 30 + 10,
+ speed: 0.5 + Math.random() * 1.5,
+ phase: Math.random() * Math.PI * 2
+ });
+ }
+ }
+
+ let blobs = [];
+ updateBlobs();
+
+ function drawMetaballs() {
+ const imageData = ctx.createImageData(width, height);
+ const data = imageData.data;
+
+ for (let i = 0; i < data.length; i += 4) {
+ data[i] = 0;
+ data[i + 1] = 0;
+ data[i + 2] = 0;
+ data[i + 3] = 255;
+ }
+
+ for (let blob of blobs) {
+ const { x, y, radius, speed, phase } = blob;
+ const time = Date.now() * speed * 0.001 + phase;
+
+ const sin = Math.sin(time);
+ const amp = 10 + 20 * sin;
+ const yVal = y + amp;
+
+ for (let dx = -radius; dx <= radius; dx++) {
+ for (let dy = -radius; dy <= radius; dy++) {
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > radius) continue;
+
+ const intensity = 1 - dist / radius;
+ const px = x + dx;
+ const py = yVal + dy;
+
+ if (px < 0 || px >= width || py < 0 || py >= height) continue;
+
+ const idx = (py * width + px) * 4;
+ const current = data[idx + 3];
+ data[idx + 3] = Math.min(current + intensity * 255, 255);
+ }
+ }
+ }
+
+ ctx.putImageData(imageData, 0, 0);
+ }
+
+ function drawGradient() {
+ const gradient = ctx.createRadialGradient(width / 2, height / 2, 0, width / 2, height / 2, Math.max(width, height));
+ for (let i = 0; i < colors[colorTheme].length; i++) {
+ gradient.addColorStop(i / (colors[colorTheme].length - 1), colors[colorTheme][i]);
+ }
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, 0, width, height);
+ }
+
+ function animate() {
+ ctx.clearRect(0, 0, width, height);
+ drawGradient();
+ drawMetaballs();
+ requestAnimationFrame(animate);
+ }
+
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/78dd0efa6a47/qwen3-14b.png b/data/artifacts/78dd0efa6a47/qwen3-14b.png
new file mode 100644
index 0000000..f150698
Binary files /dev/null and b/data/artifacts/78dd0efa6a47/qwen3-14b.png differ
diff --git a/data/artifacts/7993245ce013/claude-code.html b/data/artifacts/7993245ce013/claude-code.html
new file mode 100644
index 0000000..734ec6f
--- /dev/null
+++ b/data/artifacts/7993245ce013/claude-code.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>Memory Match</title>
+<style>
+ :root{
+ --bg1:#1e1b4b; --bg2:#312e81; --accent:#a78bfa; --accent2:#f472b6;
+ --card-back:linear-gradient(135deg,#7c3aed,#db2777);
+ --card-face:#ffffff; --matched:#34d399;
+ }
+ *{box-sizing:border-box;margin:0;padding:0}
+ body{
+ min-height:100vh;
+ font-family:'Segoe UI',system-ui,-apple-system,sans-serif;
+ background:linear-gradient(160deg,var(--bg1),var(--bg2));
+ color:#f8fafc;
+ display:flex;flex-direction:column;align-items:center;
+ padding:20px;gap:18px;
+ }
+ h1{
+ font-size:clamp(1.6rem,5vw,2.6rem);
+ font-weight:800;letter-spacing:.5px;
+ background:linear-gradient(90deg,var(--accent),var(--accent2));
+ -webkit-background-clip:text;background-clip:text;color:transparent;
+ text-align:center;
+ }
+ .controls{
+ display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:center;
+ }
+ .seg{
+ display:inline-flex;background:rgba(255,255,255,.08);
+ border-radius:999px;padding:4px;gap:4px;
+ border:1px solid rgba(255,255,255,.12);
+ }
+ .seg button{
+ border:none;background:transparent;color:#e2e8f0;
+ padding:8px 18px;border-radius:999px;cursor:pointer;
+ font-size:.95rem;font-weight:600;transition:.2s;
+ }
+ .seg button.active{background:linear-gradient(135deg,var(--accent),var(--accent2));color:#fff;box-shadow:0 4px 14px rgba(167,139,250,.4)}
+ .btn{
+ border:none;cursor:pointer;font-weight:700;font-size:.95rem;
+ padding:10px 22px;border-radius:999px;color:#fff;
+ background:linear-gradient(135deg,var(--accent),var(--accent2));
+ box-shadow:0 4px 14px rgba(244,114,182,.35);transition:transform .15s,box-shadow .15s;
+ }
+ .btn:hover{transform:translateY(-2px);box-shadow:0 8px 22px rgba(244,114,182,.5)}
+ .btn:active{transform:translateY(0)}
+ .stats{
+ display:flex;gap:26px;font-size:1.05rem;font-weight:600;
+ background:rgba(255,255,255,.07);padding:12px 26px;border-radius:16px;
+ border:1px solid rgba(255,255,255,.1);
+ }
+ .stats span{color:var(--accent);font-variant-numeric:tabular-nums}
+ .board-wrap{position:relative;width:100%;max-width:560px}
+ #board{
+ display:grid;gap:10px;perspective:900px;width:100%;
+ }
+ .card{
+ position:relative;aspect-ratio:1;cursor:pointer;
+ transform-style:preserve-3d;transition:transform .45s cubic-bezier(.4,.2,.2,1);
+ border-radius:14px;
+ }
+ .card.flipped,.card.matched{transform:rotateY(180deg)}
+ .card.matched{cursor:default}
+ .face{
+ position:absolute;inset:0;border-radius:14px;
+ display:flex;align-items:center;justify-content:center;
+ backface-visibility:hidden;-webkit-backface-visibility:hidden;
+ user-select:none;
+ }
+ .back{
+ background:var(--card-back);
+ box-shadow:0 4px 12px rgba(0,0,0,.3), inset 0 0 0 2px rgba(255,255,255,.15);
+ }
+ .back::after{content:"?";font-size:clamp(1.2rem,5vw,2rem);font-weight:800;color:rgba(255,255,255,.85)}
+ .front{
+ background:var(--card-face);color:#111;
+ transform:rotateY(180deg);
+ font-size:clamp(1.6rem,7vw,2.8rem);
+ box-shadow:0 4px 12px rgba(0,0,0,.3);
+ }
+ .card.matched .front{background:var(--matched);animation:pop .4s ease}
+ @keyframes pop{0%{transform:rotateY(180deg) scale(1)}50%{transform:rotateY(180deg) scale(1.12)}100%{transform:rotateY(180deg) scale(1)}}
+ .overlay{
+ position:fixed;inset:0;background:rgba(15,12,41,.8);
+ display:flex;align-items:center;justify-content:center;
+ opacity:0;pointer-events:none;transition:opacity .35s;backdrop-filter:blur(4px);z-index:10;
+ }
+ .overlay.show{opacity:1;pointer-events:auto}
+ .win-card{
+ background:linear-gradient(160deg,#312e81,#1e1b4b);
+ border:1px solid rgba(255,255,255,.15);
+ padding:36px 40px;border-radius:24px;text-align:center;
+ box-shadow:0 20px 60px rgba(0,0,0,.5);transform:scale(.85);transition:transform .35s;
+ max-width:90vw;
+ }
+ .overlay.show .win-card{transform:scale(1)}
+ .win-card h2{font-size:2rem;margin-bottom:8px}
+ .win-card .emoji{font-size:3rem;margin-bottom:6px}
+ .win-card p{color:#cbd5e1;margin-bottom:6px;font-size:1.05rem}
+ .win-card p b{color:var(--accent)}
+ .win-card .btn{margin-top:18px}
+</style>
+</head>
+<body>
+ <h1>🧠 Memory Match</h1>
+
+ <div class="controls">
+ <div class="seg" id="difficulty">
+ <button data-size="4" class="active">4 × 4</button>
+ <button data-size="6">6 × 6</button>
+ </div>
+ <button class="btn" id="restart">↻ Restart</button>
+ </div>
+
+ <div class="stats">
+ <div>Moves: <span id="moves">0</span></div>
+ <div>Time: <span id="time">0:00</span></div>
+ <div>Pairs: <span id="pairs">0</span></div>
+ </div>
+
+ <div class="board-wrap">
+ <div id="board"></div>
+ </div>
+
+ <div class="overlay" id="overlay">
+ <div class="win-card">
+ <div class="emoji">🎉</div>
+ <h2>You Won!</h2>
+ <p>Solved in <b id="winMoves">0</b> moves</p>
+ <p>Time: <b id="winTime">0:00</b></p>
+ <button class="btn" id="playAgain">Play Again</button>
+ </div>
+ </div>
+
+<script>
+ const EMOJIS = ['🍕','🎸','🚀','🐱','🌈','⚽','🎮','🍩','🦊','🌵','🐙','🎈',
+ '🍔','🦄','🎧','🍦','🐳','🔥','🌸','🎲','🧩','🍭','🐢','🎁'];
+
+ const board = document.getElementById('board');
+ const movesEl = document.getElementById('moves');
+ const timeEl = document.getElementById('time');
+ const pairsEl = document.getElementById('pairs');
+ const overlay = document.getElementById('overlay');
+
+ let size = 4, deck = [], first = null, lock = false;
+ let moves = 0, matched = 0, totalPairs = 0;
+ let timer = null, seconds = 0, started = false;
+
+ function fmt(s){ const m = Math.floor(s/60); const r = s%60; return m+':'+String(r).padStart(2,'0'); }
+
+ function shuffle(a){
+ for(let i=a.length-1;i>0;i--){
+ const j = Math.floor(Math.random()*(i+1));
+ [a[i],a[j]]=[a[j],a[i]];
+ }
+ return a;
+ }
+
+ function startTimer(){
+ if(started) return;
+ started = true;
+ timer = setInterval(()=>{ seconds++; timeEl.textContent = fmt(seconds); }, 1000);
+ }
+
+ function stopTimer(){ clearInterval(timer); }
+
+ function newGame(){
+ stopTimer();
+ started = false; seconds = 0; moves = 0; matched = 0; first = null; lock = false;
+ totalPairs = (size*size)/2;
+ movesEl.textContent = '0';
+ timeEl.textContent = '0:00';
+ pairsEl.textContent = '0';
+ overlay.classList.remove('show');
+
+ const chosen = shuffle([...EMOJIS]).slice(0, totalPairs);
+ deck = shuffle([...chosen, ...chosen]);
+
+ board.style.gridTemplateColumns = `repeat(${size}, 1fr)`;
+ board.innerHTML = '';
+
+ deck.forEach((emoji)=>{
+ const card = document.createElement('div');
+ card.className = 'card';
+ card.dataset.emoji = emoji;
+ card.innerHTML = `<div class="face back"></div><div class="face front">${emoji}</div>`;
+ card.addEventListener('click', ()=>flip(card));
+ board.appendChild(card);
+ });
+ }
+
+ function flip(card){
+ if(lock) return;
+ if(card.classList.contains('flipped') || card.classList.contains('matched')) return;
+
+ startTimer();
+ card.classList.add('flipped');
+
+ if(!first){ first = card; return; }
+
+ // second card
+ moves++;
+ movesEl.textContent = moves;
+
+ if(first.dataset.emoji === card.dataset.emoji){
+ first.classList.add('matched');
+ card.classList.add('matched');
+ first = null;
+ matched++;
+ pairsEl.textContent = matched;
+ if(matched === totalPairs) win();
+ } else {
+ lock = true;
+ const a = first, b = card;
+ first = null;
+ setTimeout(()=>{
+ a.classList.remove('flipped');
+ b.classList.remove('flipped');
+ lock = false;
+ }, 800);
+ }
+ }
+
+ function win(){
+ stopTimer();
+ document.getElementById('winMoves').textContent = moves;
+ document.getElementById('winTime').textContent = fmt(seconds);
+ setTimeout(()=>overlay.classList.add('show'), 500);
+ }
+
+ document.getElementById('difficulty').addEventListener('click', (e)=>{
+ const b = e.target.closest('button');
+ if(!b) return;
+ document.querySelectorAll('#difficulty button').forEach(x=>x.classList.remove('active'));
+ b.classList.add('active');
+ size = parseInt(b.dataset.size, 10);
+ newGame();
+ });
+
+ document.getElementById('restart').addEventListener('click', newGame);
+ document.getElementById('playAgain').addEventListener('click', newGame);
+
+ newGame();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/7993245ce013/claude-code.png b/data/artifacts/7993245ce013/claude-code.png
new file mode 100644
index 0000000..f11eb0d
Binary files /dev/null and b/data/artifacts/7993245ce013/claude-code.png differ
diff --git a/data/artifacts/7993245ce013/gemma3-12b.html b/data/artifacts/7993245ce013/gemma3-12b.html
new file mode 100644
index 0000000..f3630e1
--- /dev/null
+++ b/data/artifacts/7993245ce013/gemma3-12b.html
@@ -0,0 +1,206 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Memory Match</title>
+ <style>
+ body { font-family: sans-serif; text-align: center; }
+ #game-container { display: flex; justify-content: center; align-items: center; margin-top: 20px; }
+ .card { width: 50px; height: 50px; border: 1px solid #ccc; margin: 2px; box-shadow: 2px 2px 4px rgba(0,0,0,0.1); display: flex; justify-content: center; align-items: center; font-size: 36px; }
+ .card.flipped { transform: rotateY(180deg); transition: transform 0.3s ease-in-out; }
+ #controls { margin-top: 20px; }
+ #message { margin-top: 15px; font-weight: bold; }
+ #win-screen { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.8); color: white; text-align: center; padding-top: 50px; }
+ </style>
+</head>
+<body>
+
+<div id="controls">
+ <label for="difficulty">Difficulty:</label>
+ <select id="difficulty" onchange="newGame()">
+ <option value="4">4x4</option>
+ <option value="6">6x6</option>
+ </select>
+ <button onclick="newGame()">New Game</button>
+</div>
+
+<div id="message">Moves: <span id="move-count">0</span> | Time: <span id="timer">0.00</span></div>
+
+<div id="game-container"></div>
+
+<div id="win-screen">
+ <h1>Congratulations!</h1>
+ <p>You won in <span id="win-time">0.00</span> seconds with <span id="win-moves">0</span> moves!</p>
+ <button onclick="newGame()">Play Again</button>
+</div>
+
+<script>
+ let gridSize = 4;
+ let cards = [];
+ let flippedCard1 = null;
+ let flippedCard2 = null;
+ let moveCount = 0;
+ let timerInterval;
+ let startTime;
+ let gameWon = false;
+
+ const emojis = ['😀', '😂', '😍', '😎', '🤔', '😴', '🤯', '😈'];
+
+ function createBoard() {
+ cards = [];
+ const numCards = gridSize * gridSize;
+ const emojiPairs = emojis.slice(0, numCards / 2); // Only use half the emojis for pairs
+
+ for (let i = 0; i < numCards; i++) {
+ cards.push({
+ id: i,
+ emoji: emojiPairs[i % (numCards / 2)],
+ flipped: false
+ });
+ }
+ shuffle(cards); // Shuffle the cards in place
+ }
+
+ function shuffle(array) {
+ for (let i = array.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [array[i], array[j]] = [array[j], array[i]];
+ }
+ }
+
+ function renderBoard() {
+ const gameContainer = document.getElementById('game-container');
+ gameContainer.innerHTML = ''; // Clear the existing board
+
+ cards.forEach(card => {
+ const cardElement = document.createElement('div');
+ cardElement.classList.add('card');
+ cardElement.dataset.id = card.id;
+ cardElement.addEventListener('click', flipCard);
+ gameContainer.appendChild(cardElement);
+ });
+
+ updateMessage(); // Update move count and timer after rendering
+ }
+
+
+ function flipCard(event) {
+ if (gameWon) return;
+
+ const cardId = parseInt(event.currentTarget.dataset.id);
+ const card = cards[cardId];
+
+ if (card.flipped) return; // Prevent flipping already flipped cards
+
+ card.flipped = true;
+ updateCardAppearance(cardId);
+
+ if (!flippedCard1) {
+ flippedCard1 = cardId;
+ } else if (!flippedCard2) {
+ flippedCard2 = cardId;
+ setTimeout(() => checkMatch(), 500); // Delay for visual comparison
+ }
+ }
+
+
+ function updateCardAppearance(cardId) {
+ const cardElement = document.getElementById('game-container').children[cardId];
+ cardElement.classList.add('flipped');
+ }
+
+ function checkMatch() {
+ const card1 = cards[flippedCard1];
+ const card2 = cards[flippedCard2];
+
+ if (card1.emoji === card2.emoji) {
+ // Match! Keep them flipped up
+ moveCount++;
+ updateMessage();
+ } else {
+ // No match - flip back down after a short delay
+ setTimeout(() => {
+ flipBack(flippedCard1);
+ flipBack(flippedCard2);
+ resetFlips();
+ moveCount++;
+ updateMessage();
+
+ }, 300);
+ }
+ }
+
+
+ function flipBack(cardId) {
+ cards[cardId].flipped = false;
+ const cardElement = document.getElementById('game-container').children[cardId];
+ cardElement.classList.remove('flipped');
+
+ }
+
+ function resetFlips() {
+ flippedCard1 = null;
+ flippedCard2 = null;
+ }
+
+
+ function updateMessage() {
+ document.getElementById('move-count').textContent = moveCount;
+ }
+
+ function startGameTimer() {
+ startTime = new Date().getTime();
+ timerInterval = setInterval(() => {
+ const elapsedTime = (new Date().getTime() - startTime) / 1000;
+ document.getElementById('timer').textContent = elapsedTime.toFixed(2);
+ }, 100);
+ }
+
+ function stopGameTimer() {
+ clearInterval(timerInterval);
+ }
+
+
+ function newGame() {
+ gridSize = parseInt(document.getElementById('difficulty').value);
+ createBoard();
+ renderBoard();
+ moveCount = 0;
+ updateMessage();
+ resetFlips();
+
+ if (gameWon) {
+ stopGameTimer();
+ }
+
+ startGameTimer();
+ }
+
+
+ function checkWin() {
+ let allMatched = true;
+ cards.forEach(card => {
+ if (!card.flipped) {
+ allMatched = false;
+ }
+ });
+
+ if (allMatched) {
+ gameWon = true;
+ stopGameTimer();
+ const endTime = new Date().getTime();
+ const elapsedTime = (endTime - startTime) / 1000;
+ document.getElementById('win-time').textContent = elapsedTime.toFixed(2);
+ document.getElementById('win-moves').textContent = moveCount;
+ document.getElementById('win-screen').style.display = 'block';
+
+ }
+ }
+
+
+ newGame(); // Start the first game when the page loads
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/7993245ce013/gemma3-12b.png b/data/artifacts/7993245ce013/gemma3-12b.png
new file mode 100644
index 0000000..1ba65fc
Binary files /dev/null and b/data/artifacts/7993245ce013/gemma3-12b.png differ
diff --git a/data/artifacts/7993245ce013/hermes3-8b.html b/data/artifacts/7993245ce013/hermes3-8b.html
new file mode 100644
index 0000000..3cbdf5a
--- /dev/null
+++ b/data/artifacts/7993245ce013/hermes3-8b.html
@@ -0,0 +1,239 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Emoji Memory Match Game</title>
+ <style>
+ * {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ }
+
+ body {
+ font-family: Arial, sans-serif;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ background-color: #f0f0f0;
+ }
+
+ .container {
+ text-align: center;
+ }
+
+ h1 {
+ margin-bottom: 20px;
+ }
+
+ .select-container {
+ display: flex;
+ justify-content: space-around;
+ margin-bottom: 30px;
+ }
+
+ button {
+ background-color: #4CAF50;
+ border: none;
+ padding: 10px 20px;
+ text-decoration: none;
+ color: white;
+ font-size: 16px;
+ cursor: pointer;
+ transition-duration: 0.4s;
+ }
+
+ button:hover {
+ background-color: #45a049;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-template-columns: repeat(4, auto);
+ grid-gap: 10px;
+ justify-content: center;
+ align-items: center;
+ max-width: 400px;
+ }
+
+ .card {
+ background-color: #f0f0f0;
+ border: 1px solid #d9d9d9;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ cursor: pointer;
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 60px;
+ height: 80px;
+ transition-duration: 0.4s;
+ }
+
+ .card:hover {
+ box-shadow: 0 5px 12px rgba(0, 0, 0, 0.2);
+ }
+
+ .card.flip {
+ transform: rotateY(180deg);
+ background-color: white;
+ }
+
+ .card::before,
+ .card::after {
+ content: '';
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ margin-top: -5px;
+ margin-left: -5px;
+ }
+
+ .card::before {
+ background-repeat: no-repeat;
+ background-position: center;
+ transform-origin: bottom left;
+ transition-duration: 0.4s;
+ }
+
+ .card::after {
+ background-repeat: no-repeat;
+ background-position: center;
+ transform-origin: top right;
+ transition-duration: 0.4s;
+ }
+
+ span {
+ display: none;
+ }
+
+ </style>
+</head>
+<body>
+
+ <div class="container">
+ <h1>Emoji Memory Match Game</h1>
+ <div class="select-container">
+ <button onclick="setDifficulty(4)">4x4</button>
+ <button onclick="setDifficulty(6)">6x6</button>
+ </div>
+ <div class="grid-container"></div>
+ <div id="overlay" style="display: none;">
+ <h2>Congratulations! You won!</h2>
+ <button onclick="location.reload()">Restart</button>
+ </div>
+ </div>
+
+ <script>
+ const emojis = [
+ { src: "emoji1.jpg", name: "🌞" },
+ { src: "emoji2.jpg", name: "🌈" },
+ // Add more emoji pairs here
+ { src: "emoji6.jpg", name: "✨" },
+ { src: "emoji7.jpg", name: "😄" }
+ ];
+
+ let difficulty = 4;
+ const cardsPerRow = {
+ 4: 4,
+ 6: 6
+ };
+
+ let flippedCards = [];
+ let movesCounter = 0;
+
+ function setDifficulty(value) {
+ difficulty = value;
+ createGrid();
+ }
+
+ function shuffleArray(array) {
+ for (let i = array.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random(i));
+ [array[i], array[j]] = [array[j], array[i]];
+ }
+ }
+
+ function createGrid() {
+ const gridContainer = document.querySelector(".grid-container");
+
+ // Clear existing cards
+ gridContainer.innerHTML = '';
+
+ shuffleArray(emojis);
+
+ let cardsPlaced = 0;
+
+ while (cardsPlaced < difficulty * 2) {
+ const cardFront = document.createElement("div");
+ const cardBack = document.createElement("div");
+
+ cardFront.classList.add("card", "flip");
+ cardBack.classList.add("card");
+
+ // Add emoji to the card front
+ cardFront.style.backgroundImage = `url(${emojis[cardsPlaced].src})`;
+ cardBack.innerHTML = `<span>${emojis[cardsPlaced].name}</span>`;
+
+ const cardPair = document.createElement("div");
+ cardPair.classList.add("card-container");
+
+ cardPair.appendChild(cardFront);
+ cardBack.addEventListener("click", () => {
+ flipCard(cardBack, cardFront, cardPair);
+ });
+ cardPair.appendChild(cardBack);
+
+ gridContainer.appendChild(cardPair);
+
+ cardsPlaced++;
+ }
+ }
+
+ function flipCard(cardBack, cardFront, cardPair) {
+ if (flippedCards.length < 2) {
+ cardFront.classList.add("flip");
+
+ if (flippedCards.length === 1) {
+ setTimeout(() => {
+ cardPair.appendChild(cardBack);
+ }, 1000);
+
+ flippedCards.push(cardBack);
+ } else {
+ const firstCard = flippedCards[0];
+ const secondCard = flippedCards[1];
+
+ // Check if the cards match
+ if (firstCard.querySelector("span").textContent === secondCard.querySelector("span").textContent) {
+ firstCard.classList.remove("flip");
+ secondCard.classList.remove("flip");
+
+ setTimeout(() => {
+ cardPair.style.display = "none";
+ }, 1000);
+
+ flippedCards.length = 0;
+ movesCounter++;
+ } else {
+ cardFront.classList.remove("flip");
+
+ setTimeout(() => {
+ cardPair.appendChild(cardBack);
+ }, 1000);
+
+ flippedCards.length = 1;
+ }
+
+ document.querySelector(".grid-container").appendChild(cardPair);
+ }
+ }
+ }
+
+ createGrid();
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/7993245ce013/hermes3-8b.png b/data/artifacts/7993245ce013/hermes3-8b.png
new file mode 100644
index 0000000..fae3bf1
Binary files /dev/null and b/data/artifacts/7993245ce013/hermes3-8b.png differ
diff --git a/data/artifacts/7993245ce013/hf-qwen-coder-32b.html b/data/artifacts/7993245ce013/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..a4129a4
--- /dev/null
+++ b/data/artifacts/7993245ce013/hf-qwen-coder-32b.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Memory Match Game</title>
+<style>
+body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f4f4f9; margin: 0; }
+#game-container { text-align: center; }
+#difficulty-selector { margin-bottom: 20px; }
+#timer, #moves { font-size: 1.2em; margin-bottom: 20px; }
+.grid { display: grid; gap: 10px; }
+.card { width: 50px; height: 50px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); display: flex; justify-content: center; align-items: center; font-size: 2em; cursor: pointer; transition: transform 0.5s; }
+.card.flipped { transform: rotateY(180deg); }
+.card.matched { background-color: #c8e6c9; }
+#win-screen { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); color: white; font-size: 2em; justify-content: center; align-items: center; flex-direction: column; }
+#win-screen button { margin-top: 20px; padding: 10px 20px; font-size: 1em; cursor: pointer; }
+</style>
+</head>
+<body>
+<div id="game-container">
+ <div id="difficulty-selector">
+ <label for="difficulty">Select Difficulty:</label>
+ <select id="difficulty">
+ <option value="4x4">4x4</option>
+ <option value="6x6">6x6</option>
+ </select>
+ </div>
+ <div id="timer">Time: 0s</div>
+ <div id="moves">Moves: 0</div>
+ <div id="grid" class="grid"></div>
+ <div id="win-screen">
+ <p>Congratulations! You've won!</p>
+ <button onclick="restartGame()">Play Again</button>
+ </div>
+</div>
+<script>
+const grid = document.getElementById('grid');
+const timerDisplay = document.getElementById('timer');
+const movesDisplay = document.getElementById('moves');
+const difficultySelector = document.getElementById('difficulty');
+const winScreen = document.getElementById('win-screen');
+
+let cards = [];
+let flippedCards = [];
+let matchedPairs = 0;
+let moves = 0;
+let time = 0;
+let timerInterval;
+let gridSize;
+
+function startGame() {
+ clearInterval(timerInterval);
+ time = 0;
+ moves = 0;
+ matchedPairs = 0;
+ timerDisplay.textContent = 'Time: 0s';
+ movesDisplay.textContent = 'Moves: 0';
+ winScreen.style.display = 'none';
+
+ gridSize = difficultySelector.value === '4x4' ? 16 : 36;
+ grid.style.gridTemplateColumns = `repeat(${Math.sqrt(gridSize)}, 1fr)`;
+
+ const emojis = Array.from({ length: gridSize / 2 }, (_, i) => String.fromCharCode(0x1F600 + i)).flatMap(e => [e, e]);
+ cards = shuffleArray(emojis);
+
+ grid.innerHTML = '';
+ for (let card of cards) {
+ const div = document.createElement('div');
+ div.classList.add('card');
+ div.dataset.emoji = card;
+ div.addEventListener('click', flipCard);
+ grid.appendChild(div);
+ }
+
+ timerInterval = setInterval(() => {
+ time++;
+ timerDisplay.textContent = `Time: ${time}s`;
+ }, 1000);
+}
+
+function shuffleArray(array) {
+ for (let i = array.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [array[i], array[j]] = [array[j], array[i]];
+ }
+ return array;
+}
+
+function flipCard(event) {
+ if (flippedCards.length >= 2 || flippedCards.includes(this)) return;
+
+ this.classList.add('flipped');
+ flippedCards.push(this);
+
+ if (flippedCards.length === 2) {
+ moves++;
+ movesDisplay.textContent = `Moves: ${moves}`;
+
+ const [first, second] = flippedCards;
+ if (first.dataset.emoji === second.dataset.emoji) {
+ first.classList.add('matched');
+ second.classList.add('matched');
+ matchedPairs++;
+
+ if (matchedPairs * 2 === gridSize) {
+ clearInterval(timerInterval);
+ winScreen.style.display = 'flex';
+ }
+ } else {
+ setTimeout(() => {
+ first.classList.remove('flipped');
+ second.classList.remove('flipped');
+ }, 1000);
+ }
+
+ flippedCards = [];
+ }
+}
+
+function restartGame() {
+ startGame();
+}
+
+difficultySelector.addEventListener('change', startGame);
+startGame();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/7993245ce013/hf-qwen-coder-32b.png b/data/artifacts/7993245ce013/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..b858b7a
Binary files /dev/null and b/data/artifacts/7993245ce013/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/7993245ce013/qwen25-7b.html b/data/artifacts/7993245ce013/qwen25-7b.html
new file mode 100644
index 0000000..31f2d8d
--- /dev/null
+++ b/data/artifacts/7993245ce013/qwen25-7b.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Memory Match Game</title>
+ <style>
+ body { font-family: Arial, sans-serif; text-align: center; }
+ .grid { display: grid; grid-template-columns: repeat(4, 50px); gap: 5px; margin-top: 20px; }
+ .card { width: 50px; height: 50px; background-color: #ddd; border-radius: 10px; cursor: pointer; transition: transform 0.3s; }
+ .move, .timer { margin-bottom: 10px; font-size: 20px; }
+ .win-screen { display: none; font-size: 24px; margin-top: 20px; }
+ </style>
+</head>
+<body>
+ <h1>Memory Match Game</h1>
+ <select id="difficulty">
+ <option value="4x4">4x4 (Easy)</option>
+ <option value="6x6" selected>6x6 (Normal)</option>
+ </select>
+ <div class="grid" id="grid"></div>
+ <div class="move">Moves: <span id="moves">0</span></div>
+ <div class="timer">Time: <span id="time">00:00</span></div>
+ <div class="win-screen" id="winScreen">You win!</div>
+
+ <script>
+ const cards = ['🔥', '💧', '🌱', '🌈', '🌟', '💫'].concat(['🔥', '💧', '🌱', '🌈', '🌟', '💫']);
+ let grid = document.getElementById('grid');
+ let moves = 0;
+ let time = 0;
+ let startTime, timer;
+
+ function startGame(difficulty) {
+ clearInterval(timer);
+ moves = 0;
+ time = 0;
+ document.getElementById('moves').textContent = moves;
+ document.getElementById('time').textContent = '00:00';
+ grid.innerHTML = '';
+ cards.sort(() => Math.random() - 0.5).slice(0, difficulty * 2).forEach(card => {
+ let cardEl = document.createElement('div');
+ cardEl.className = 'card';
+ cardEl.dataset.value = card;
+ cardEl.addEventListener('click', flipCard);
+ grid.appendChild(cardEl);
+ });
+ }
+
+ function flipCard(e) {
+ const card = e.target;
+ if (card.classList.contains('flipped') || card.classList.contains('matched')) return;
+ card.classList.add('flipped');
+ moves++;
+ document.getElementById('moves').textContent = moves;
+
+ if (grid.querySelectorAll('.flipped:not(.matched)').length === 2) {
+ setTimeout(checkMatch, 500);
+ }
+
+ time++;
+ document.getElementById('time').textContent = formatTime(time);
+ }
+
+ function checkMatch() {
+ const flippedCards = grid.querySelectorAll('.flipped');
+ let [card1, card2] = Array.from(flippedCards);
+
+ if (card1.dataset.value === card2.dataset.value) {
+ card1.classList.add('matched');
+ card2.classList.add('matched');
+ setTimeout(() => {
+ card1.removeEventListener('click', flipCard);
+ card2.removeEventListener('click', flipCard);
+ }, 500);
+ } else {
+ setTimeout(() => {
+ card1.classList.remove('flipped');
+ card2.classList.remove('flipped');
+ }, 700);
+ }
+
+ if (grid.querySelectorAll('.matched').length === cards.length) {
+ clearInterval(timer);
+ document.getElementById('winScreen').style.display = 'block';
+ document.getElementById('time').textContent = formatTime(time);
+ }
+ }
+
+ function formatTime(seconds) {
+ const minutes = Math.floor(seconds / 60);
+ const secs = seconds % 60;
+ return `${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
+ }
+
+ startGame(document.getElementById('difficulty').value === '4x4' ? 4 : 6);
+
+ document.getElementById('difficulty').addEventListener('change', () => {
+ startGame(document.getElementById('difficulty').value === '4x4' ? 4 : 6);
+ });
+
+ startTime = Date.now();
+ timer = setInterval(() => time++, 1000);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/7993245ce013/qwen25-7b.png b/data/artifacts/7993245ce013/qwen25-7b.png
new file mode 100644
index 0000000..fda4268
Binary files /dev/null and b/data/artifacts/7993245ce013/qwen25-7b.png differ
diff --git a/data/artifacts/7993245ce013/qwen3-14b.html b/data/artifacts/7993245ce013/qwen3-14b.html
new file mode 100644
index 0000000..2a8d989
--- /dev/null
+++ b/data/artifacts/7993245ce013/qwen3-14b.html
@@ -0,0 +1,215 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Memory Match Game</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ background: #222;
+ color: #fff;
+ text-align: center;
+ margin: 0;
+ padding: 20px;
+ }
+ h1 {
+ margin-bottom: 10px;
+ }
+ #controls {
+ margin-bottom: 20px;
+ }
+ #difficulty {
+ padding: 8px 16px;
+ font-size: 16px;
+ }
+ #game {
+ display: grid;
+ grid-template-columns: repeat(var(--cols), 100px);
+ gap: 10px;
+ justify-content: center;
+ margin: 0 auto;
+ max-width: 600px;
+ }
+ .card {
+ width: 100px;
+ height: 100px;
+ background-color: #444;
+ border: 2px solid #666;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: transform 0.6s, background-color 0.3s;
+ position: relative;
+ overflow: hidden;
+ }
+ .card.flipped {
+ background-color: #333;
+ transform: rotateY(180deg);
+ }
+ .card.flipped::after {
+ content: attr(data-emoji);
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 48px;
+ transform: rotateY(180deg);
+ }
+ .card.matched {
+ background-color: #2a9d8f;
+ border-color: #26a69a;
+ transform: scale(1.05);
+ }
+ #info {
+ margin: 20px 0;
+ font-size: 18px;
+ }
+ #win-screen {
+ display: none;
+ font-size: 24px;
+ margin-top: 30px;
+ }
+ #restart {
+ margin-top: 10px;
+ padding: 10px 20px;
+ font-size: 16px;
+ background-color: #2a9d8f;
+ color: #fff;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ }
+ #restart:hover {
+ background-color: #26a69a;
+ }
+ </style>
+</head>
+<body>
+ <h1>Memory Match Game</h1>
+ <div id="controls">
+ <label for="difficulty">Difficulty:</label>
+ <select id="difficulty">
+ <option value="4">4x4</option>
+ <option value="6" selected>6x6</option>
+ </select>
+ </div>
+ <div id="game"></div>
+ <div id="info">
+ <span id="moves">Moves: 0</span> | <span id="timer">Time: 0s</span>
+ </div>
+ <div id="win-screen">
+ <span id="win-message">🎉 You Win! 🎉</span>
+ <button id="restart">Restart Game</button>
+ </div>
+
+ <script>
+ const difficultySelect = document.getElementById('difficulty');
+ const gameContainer = document.getElementById('game');
+ const movesDisplay = document.getElementById('moves');
+ const timerDisplay = document.getElementById('timer');
+ const winScreen = document.getElementById('win-screen');
+ const restartButton = document.getElementById('restart');
+
+ let cards = [];
+ let firstCard = null;
+ let secondCard = null;
+ let moves = 0;
+ let matchedPairs = 0;
+ let startTime;
+ let timerInterval;
+ let cols = 6;
+
+ const emojiPairs = [
+ "😀", "😀", "😎", "😎", "🥳", "🥳", "🔥", "🔥", "💥", "💥", "🌟", "🌟",
+ "🍕", "🍕", "🍔", "🍔", "🍩", "🍩", "🍦", "🍦", "🍪", "🍪", "🍵", "🍵",
+ "🐱", "🐱", "🐶", "🐶", "🐭", "🐭", "🐹", "🐹", "🐰", "🐰", "🦊", "🦊",
+ "🍎", "🍎", "🍇", "🍇", "🍓", "🍓", "🍊", "🍊", "🍋", "🍋", "🍉", "🍉"
+ ];
+
+ function createBoard() {
+ gameContainer.innerHTML = '';
+ cards = [];
+ const size = cols * cols;
+ const emojis = [...emojiPairs].sort(() => 0.5 - Math.random()).slice(0, size);
+ let cardIndex = 0;
+
+ for (let i = 0; i < cols * cols; i++) {
+ const card = document.createElement('div');
+ card.classList.add('card');
+ card.dataset.emoji = emojis[cardIndex++];
+ card.addEventListener('click', handleCardClick);
+ gameContainer.appendChild(card);
+ cards.push(card);
+ }
+ resetGame();
+ }
+
+ function resetGame() {
+ cards.forEach(card => {
+ card.classList.remove('flipped', 'matched');
+ });
+ firstCard = null;
+ secondCard = null;
+ moves = 0;
+ matchedPairs = 0;
+ movesDisplay.textContent = `Moves: 0`;
+ timerDisplay.textContent = `Time: 0s`;
+ winScreen.style.display = 'none';
+ startTime = Date.now();
+ if (timerInterval) clearInterval(timerInterval);
+ timerInterval = setInterval(() => {
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
+ timerDisplay.textContent = `Time: ${elapsed}s`;
+ }, 1000);
+ }
+
+ function handleCardClick(e) {
+ const card = e.currentTarget;
+ if (card.classList.contains('flipped') || card.classList.contains('matched')) return;
+
+ card.classList.add('flipped');
+ if (!firstCard) {
+ firstCard = card;
+ } else if (!secondCard) {
+ secondCard = card;
+ moves++;
+ movesDisplay.textContent = `Moves: ${moves}`;
+ setTimeout(() => {
+ if (firstCard.dataset.emoji === secondCard.dataset.emoji) {
+ firstCard.classList.add('matched');
+ secondCard.classList.add('matched');
+ matchedPairs++;
+ if (matchedPairs === (cols * cols) / 2) {
+ endGame();
+ }
+ } else {
+ firstCard.classList.remove('flipped');
+ secondCard.classList.remove('flipped');
+ }
+ firstCard = null;
+ secondCard = null;
+ }, 500);
+ }
+ }
+
+ function endGame() {
+ clearInterval(timerInterval);
+ winScreen.style.display = 'block';
+ }
+
+ difficultySelect.addEventListener('change', () => {
+ cols = parseInt(difficultySelect.value);
+ createBoard();
+ });
+
+ createBoard();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/7993245ce013/qwen3-14b.png b/data/artifacts/7993245ce013/qwen3-14b.png
new file mode 100644
index 0000000..920b7bc
Binary files /dev/null and b/data/artifacts/7993245ce013/qwen3-14b.png differ
diff --git a/data/artifacts/a03b18e65a8c/claude-code.html b/data/artifacts/a03b18e65a8c/claude-code.html
new file mode 100644
index 0000000..4d24ee9
--- /dev/null
+++ b/data/artifacts/a03b18e65a8c/claude-code.html
@@ -0,0 +1,312 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>DVD Bounce — Elevated</title>
+<style>
+ :root{
+ --bg:#05060a;
+ --ink:#e8ecf5;
+ --accent:#7c5cff;
+ }
+ *{box-sizing:border-box;margin:0;padding:0}
+ html,body{height:100%;overflow:hidden}
+ body{
+ background:radial-gradient(120% 120% at 50% 0%, #0d1020 0%, #04050a 60%, #000 100%);
+ color:var(--ink);
+ font-family:"Segoe UI",system-ui,-apple-system,sans-serif;
+ -webkit-font-smoothing:antialiased;
+ }
+ #stage{position:fixed;inset:0;display:block}
+ /* HUD */
+ .hud{
+ position:fixed;top:16px;left:16px;z-index:10;
+ display:flex;flex-direction:column;gap:10px;
+ pointer-events:none;
+ }
+ .panel{
+ pointer-events:auto;
+ background:rgba(18,20,34,.55);
+ backdrop-filter:blur(12px) saturate(140%);
+ -webkit-backdrop-filter:blur(12px) saturate(140%);
+ border:1px solid rgba(255,255,255,.09);
+ border-radius:14px;
+ padding:14px 16px;
+ box-shadow:0 10px 40px rgba(0,0,0,.45);
+ }
+ .stats{display:flex;gap:22px;align-items:center}
+ .stat{display:flex;flex-direction:column;line-height:1.1}
+ .stat .num{font-size:26px;font-weight:800;letter-spacing:.5px;font-variant-numeric:tabular-nums}
+ .stat .lbl{font-size:10px;text-transform:uppercase;letter-spacing:2px;opacity:.55;margin-top:3px}
+ .corner .num{color:#ffd35c;text-shadow:0 0 18px rgba(255,211,92,.55)}
+ .controls{display:flex;gap:8px;flex-wrap:wrap}
+ button{
+ pointer-events:auto;
+ font:inherit;font-size:12px;font-weight:600;letter-spacing:.4px;
+ color:var(--ink);
+ background:linear-gradient(180deg,rgba(124,92,255,.35),rgba(124,92,255,.15));
+ border:1px solid rgba(124,92,255,.5);
+ border-radius:10px;
+ padding:9px 14px;cursor:pointer;
+ transition:transform .08s ease, background .15s ease, box-shadow .15s ease;
+ }
+ button:hover{background:linear-gradient(180deg,rgba(124,92,255,.55),rgba(124,92,255,.25));box-shadow:0 0 22px rgba(124,92,255,.4)}
+ button:active{transform:translateY(1px) scale(.98)}
+ button.ghost{background:rgba(255,255,255,.05);border-color:rgba(255,255,255,.14)}
+ button.ghost:hover{background:rgba(255,255,255,.1);box-shadow:none}
+ .hint{
+ position:fixed;bottom:14px;right:16px;z-index:10;
+ font-size:11px;opacity:.4;letter-spacing:.5px;pointer-events:none;
+ }
+ .flash{animation:pop .4s ease}
+ @keyframes pop{0%{transform:scale(1)}35%{transform:scale(1.28)}100%{transform:scale(1)}}
+</style>
+</head>
+<body>
+<canvas id="stage"></canvas>
+
+<div class="hud">
+ <div class="panel stats">
+ <div class="stat corner">
+ <span class="num" id="cornerCount">0</span>
+ <span class="lbl">Corners</span>
+ </div>
+ <div class="stat">
+ <span class="num" id="wallCount">0</span>
+ <span class="lbl">Wall hits</span>
+ </div>
+ <div class="stat">
+ <span class="num" id="logoCount">1</span>
+ <span class="lbl">Logos</span>
+ </div>
+ </div>
+ <div class="panel controls">
+ <button id="addBtn">+ Add logo</button>
+ <button id="chaosBtn">Chaos ×5</button>
+ <button class="ghost" id="resetBtn">Reset</button>
+ </div>
+</div>
+
+<div class="hint">corner hits trigger the confetti · click canvas to spawn</div>
+
+<script>
+(() => {
+ const canvas = document.getElementById('stage');
+ const ctx = canvas.getContext('2d');
+
+ const cornerEl = document.getElementById('cornerCount');
+ const wallEl = document.getElementById('wallCount');
+ const logoEl = document.getElementById('logoCount');
+
+ let W = 0, H = 0, DPR = 1;
+ function resize() {
+ DPR = Math.min(window.devicePixelRatio || 1, 2);
+ W = window.innerWidth;
+ H = window.innerHeight;
+ canvas.width = Math.floor(W * DPR);
+ canvas.height = Math.floor(H * DPR);
+ canvas.style.width = W + 'px';
+ canvas.style.height = H + 'px';
+ ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
+ }
+ window.addEventListener('resize', resize);
+ resize();
+
+ // ---- palette ----
+ const PALETTE = [
+ '#ff4d6d','#ffd35c','#4ade80','#38bdf8','#7c5cff',
+ '#f472b6','#fb923c','#22d3ee','#a3e635','#e879f9'
+ ];
+ const rand = (a, b) => a + Math.random() * (b - a);
+ const pick = arr => arr[(Math.random() * arr.length) | 0];
+ const nextColor = cur => { let c; do { c = pick(PALETTE); } while (c === cur); return c; };
+
+ // ---- state ----
+ let logos = [];
+ let particles = [];
+ let cornerHits = 0, wallHits = 0;
+
+ // The DVD wordmark, drawn once to an offscreen canvas and tinted per-logo.
+ function makeLogoSprite() {
+ const w = 150, h = 78;
+ const c = document.createElement('canvas');
+ c.width = w; c.height = h;
+ const g = c.getContext('2d');
+
+ g.fillStyle = '#fff';
+ g.textAlign = 'center';
+ g.textBaseline = 'middle';
+
+ // "DVD"
+ g.font = 'italic 900 52px "Arial Black", Arial, sans-serif';
+ g.save();
+ g.translate(w/2, h*0.36);
+ g.scale(1, 0.82);
+ g.fillText('DVD', 0, 0);
+ g.restore();
+
+ // squashed ellipse under it (the classic logo oval)
+ g.beginPath();
+ g.ellipse(w/2, h*0.70, 46, 12, 0, 0, Math.PI*2);
+ g.fill();
+ // knock out the label bar
+ g.save();
+ g.globalCompositeOperation = 'destination-out';
+ g.fillRect(w/2 - 34, h*0.66, 68, 8);
+ g.restore();
+ // small "video" text on the bar
+ g.font = '900 11px Arial, sans-serif';
+ g.fillText('V I D E O', w/2, h*0.70);
+
+ return c;
+ }
+ const SPRITE = makeLogoSprite();
+ const LW = SPRITE.width, LH = SPRITE.height;
+
+ function spawnLogo(x, y) {
+ const speed = rand(2.2, 3.6);
+ const ang = rand(0, Math.PI * 2);
+ logos.push({
+ x: x != null ? x : rand(0, Math.max(0, W - LW)),
+ y: y != null ? y : rand(0, Math.max(0, H - LH)),
+ vx: Math.cos(ang) * speed * (Math.random() < .5 ? 1 : -1),
+ vy: Math.sin(ang) * speed * (Math.random() < .5 ? 1 : -1),
+ color: pick(PALETTE),
+ trail: []
+ });
+ updateHUD();
+ }
+
+ function burst(x, y, big) {
+ const n = big ? 90 : 22;
+ for (let i = 0; i < n; i++) {
+ const a = Math.random() * Math.PI * 2;
+ const sp = rand(big ? 2 : 1, big ? 9 : 4);
+ particles.push({
+ x, y,
+ vx: Math.cos(a) * sp,
+ vy: Math.sin(a) * sp - (big ? 1.5 : 0),
+ life: 1,
+ decay: rand(0.012, 0.03),
+ size: rand(2, big ? 6 : 4),
+ color: pick(PALETTE),
+ spin: rand(-0.3, 0.3),
+ rot: Math.random() * Math.PI
+ });
+ }
+ }
+
+ function flash(el){ el.classList.remove('flash'); void el.offsetWidth; el.classList.add('flash'); }
+ function updateHUD() {
+ cornerEl.textContent = cornerHits;
+ wallEl.textContent = wallHits;
+ logoEl.textContent = logos.length;
+ }
+
+ // ---- main loop ----
+ function frame() {
+ // fade previous frame → motion trail
+ ctx.fillStyle = 'rgba(4,5,10,0.20)';
+ ctx.fillRect(0, 0, W, H);
+
+ const maxX = W - LW, maxY = H - LH;
+
+ for (const L of logos) {
+ L.x += L.vx;
+ L.y += L.vy;
+
+ let hitX = false, hitY = false;
+ if (L.x <= 0) { L.x = 0; L.vx = Math.abs(L.vx); hitX = true; }
+ else if (L.x >= maxX) { L.x = maxX; L.vx = -Math.abs(L.vx); hitX = true; }
+ if (L.y <= 0) { L.y = 0; L.vy = Math.abs(L.vy); hitY = true; }
+ else if (L.y >= maxY) { L.y = maxY; L.vy = -Math.abs(L.vy); hitY = true; }
+
+ if (hitX || hitY) {
+ L.color = nextColor(L.color);
+ if (hitX && hitY) {
+ // CORNER!
+ cornerHits++;
+ burst(L.x + LW/2, L.y + LH/2, true);
+ flash(cornerEl);
+ } else {
+ wallHits++;
+ burst(hitX ? (L.x <= 0 ? 0 : W) : L.x + LW/2,
+ hitY ? (L.y <= 0 ? 0 : H) : L.y + LH/2, false);
+ }
+ updateHUD();
+ }
+
+ // trail
+ L.trail.push({ x: L.x, y: L.y });
+ if (L.trail.length > 14) L.trail.shift();
+ for (let i = 0; i < L.trail.length; i++) {
+ const t = L.trail[i];
+ const alpha = (i / L.trail.length) * 0.28;
+ drawSprite(t.x, t.y, L.color, alpha);
+ }
+
+ drawSprite(L.x, L.y, L.color, 1);
+ }
+
+ // particles
+ for (let i = particles.length - 1; i >= 0; i--) {
+ const p = particles[i];
+ p.vy += 0.08; // gravity
+ p.vx *= 0.99;
+ p.x += p.vx;
+ p.y += p.vy;
+ p.rot += p.spin;
+ p.life -= p.decay;
+ if (p.life <= 0) { particles.splice(i, 1); continue; }
+
+ ctx.save();
+ ctx.globalAlpha = Math.max(0, p.life);
+ ctx.translate(p.x, p.y);
+ ctx.rotate(p.rot);
+ ctx.fillStyle = p.color;
+ ctx.shadowColor = p.color;
+ ctx.shadowBlur = 8;
+ const s = p.size;
+ ctx.fillRect(-s/2, -s/2, s, s * 1.6);
+ ctx.restore();
+ }
+
+ requestAnimationFrame(frame);
+ }
+
+ function drawSprite(x, y, color, alpha) {
+ ctx.save();
+ ctx.globalAlpha = alpha;
+ // tint: draw white sprite then multiply the color through
+ ctx.drawImage(SPRITE, x, y);
+ ctx.globalCompositeOperation = 'source-atop';
+ ctx.globalAlpha = alpha;
+ ctx.fillStyle = color;
+ ctx.fillRect(x, y, LW, LH);
+ ctx.restore();
+ }
+
+ // ---- controls ----
+ document.getElementById('addBtn').addEventListener('click', () => spawnLogo());
+ document.getElementById('chaosBtn').addEventListener('click', () => {
+ for (let i = 0; i < 5; i++) spawnLogo();
+ });
+ document.getElementById('resetBtn').addEventListener('click', () => {
+ logos = []; particles = [];
+ cornerHits = 0; wallHits = 0;
+ spawnLogo(W/2 - LW/2, H/2 - LH/2);
+ });
+ canvas.addEventListener('pointerdown', e => spawnLogo(
+ Math.min(Math.max(0, e.clientX - LW/2), W - LW),
+ Math.min(Math.max(0, e.clientY - LH/2), H - LH)
+ ));
+
+ // ---- go ----
+ spawnLogo(W/2 - LW/2, H/2 - LH/2);
+ updateHUD();
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a03b18e65a8c/claude-code.png b/data/artifacts/a03b18e65a8c/claude-code.png
new file mode 100644
index 0000000..656658b
Binary files /dev/null and b/data/artifacts/a03b18e65a8c/claude-code.png differ
diff --git a/data/artifacts/a03b18e65a8c/gemma3-12b.html b/data/artifacts/a03b18e65a8c/gemma3-12b.html
new file mode 100644
index 0000000..1ed2418
--- /dev/null
+++ b/data/artifacts/a03b18e65a8c/gemma3-12b.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Bouncing DVD Logo</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: #222; }
+ canvas { display: block; }
+ </style>
+</head>
+<body>
+ <canvas id="myCanvas"></canvas>
+ <button id="chaosButton">Add Logo!</button>
+
+ <script>
+ const canvas = document.getElementById('myCanvas');
+ const ctx = canvas.getContext('2d');
+ document.getElementById('chaosButton').addEventListener('click', addLogo);
+
+
+ let logos = [];
+ let counter = 0;
+ let chaosLevel = 0;
+
+ function init() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ addLogo(); //Initial logo creation
+ }
+
+ function addLogo() {
+ for (let i = 0; i < chaosLevel + 1; i++) {
+ const x = Math.random() * canvas.width;
+ const y = Math.random() * canvas.height;
+ const size = 20 + Math.random() * 30;
+ logos.push({x, y, dx: (Math.random() - 0.5) * 4, dy: (Math.random() - 0.5) * 4, radius: size, color: getRandomColor(), trailLength: Math.max(10, Math.round(size / 2)), trailPoints: []});
+ }
+ }
+
+
+ function getRandomColor() {
+ const hue = Math.floor(Math.random() * 360);
+ return `hsl(${hue}, 70%, 50%)`;
+ }
+
+ let lastTime = 0;
+
+ function animate(timestamp) {
+ requestAnimationFrame(animate);
+ const deltaTime = (timestamp - lastTime) / 1000;
+ lastTime = timestamp;
+
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = 0; i < logos.length; i++) {
+ const logo = logos[i];
+ logo.x += logo.dx * deltaTime;
+ logo.y += logo.dy * deltaTime;
+
+
+ // Wall collisions
+ if (logo.x + logo.radius > canvas.width || logo.x - logo.radius < 0) {
+ logo.dx = -logo.dx;
+ logo.color = getRandomColor();
+ }
+ if (logo.y + logo.radius > canvas.height || logo.y - logo.radius < 0) {
+ logo.dy = -logo.dy;
+ logo.color = getRandomColor();
+ }
+
+ // Corner collisions - celebratory particle burst and counter
+ if ((logo.x + logo.radius > canvas.width && logo.y + logo.radius > canvas.height) ||
+ (logo.x - logo.radius < 0 && logo.y - logo.radius < 0) ||
+ (logo.x + logo.radius > canvas.width && logo.y - logo.radius < 0) ||
+ (logo.x - logo.radius < 0 && logo.y + logo.radius > canvas.height)) {
+ counter++;
+ //Particle burst (simplified)
+ for (let j = 0; j < 15; j++) {
+ const angle = Math.random() * Math.PI * 2;
+ const speed = Math.random() * 3 + 1;
+ ctx.beginPath();
+ ctx.arc(logo.x, logo.y, 2, 0, Math.PI * 2);
+ ctx.closePath();
+
+ }
+
+
+ }
+
+
+
+ // Motion trail
+ logo.trailPoints.push({ x: logo.x, y: logo.y });
+ if (logo.trailPoints.length > logo.trailLength) {
+ logo.trailPoints.shift();
+ }
+
+ ctx.beginPath();
+ ctx.arc(logo.x, logo.y, logo.radius, 0, Math.PI * 2);
+ ctx.fillStyle = logo.color;
+ ctx.fill();
+
+ // Draw trail
+ ctx.strokeStyle = logo.color;
+ ctx.globalAlpha = (logo.trailPoints.length / logo.trailLength) ; // Fade out trail
+ ctx.lineWidth = logo.radius * 0.5;
+ ctx.beginPath();
+ ctx.moveTo(logo.trailPoints[0].x, logo.trailPoints[0].y);
+ for (let k = 1; k < logo.trailPoints.length; k++) {
+ ctx.lineTo(logo.trailPoints[k].x, logo.trailPoints[k].y);
+ }
+ ctx.stroke();
+
+ ctx.globalAlpha = 1; //Reset alpha for next logo
+
+ }
+
+
+
+ //Display Counter
+ ctx.fillStyle = 'white';
+ ctx.font = '20px sans-serif';
+ ctx.fillText('Corner Hits: ' + counter, 10, 30);
+
+
+ }
+
+
+ chaosLevel = 1; //Initial chaos level
+
+ init();
+ requestAnimationFrame(animate);
+
+
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a03b18e65a8c/gemma3-12b.png b/data/artifacts/a03b18e65a8c/gemma3-12b.png
new file mode 100644
index 0000000..8d1cdb1
Binary files /dev/null and b/data/artifacts/a03b18e65a8c/gemma3-12b.png differ
diff --git a/data/artifacts/a03b18e65a8c/hermes3-8b.html b/data/artifacts/a03b18e65a8c/hermes3-8b.html
new file mode 100644
index 0000000..0839821
--- /dev/null
+++ b/data/artifacts/a03b18e65a8c/hermes3-8b.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Bouncing DVD Logo</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ }
+ canvas {
+ display: block;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="gameCanvas"></canvas>
+ <button id="addLogo">Add Logo</button>
+ <script>
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+
+ let logoImg = new Image();
+ logoImg.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAABCAIAAADPSFZ ASAADJklSNTbAAAACXBIWXMAAASUJAAAOjBgjfQAAA3NJREFCQ0BJQWxKAAMjPAeTm+//9/gNubzCw8D7f/z4L7+/v79++/z5syZGdOeYhAAAAAElFTkSuQmCC";
+
+ let logo = {
+ x: 0,
+ y: 0,
+ width: 50,
+ height: 50,
+ speedX: 2,
+ speedY: 3,
+ color: 'red',
+ trailColor: 'white',
+ trailLength: 10
+ };
+
+ let gameLoop = setInterval(() => {
+ drawBackground();
+ drawLogo();
+
+ logo.x += logo.speedX;
+ logo.y += logo.speedY;
+
+ if (logo.x + logo.width > canvas.width || logo.x < 0) {
+ logo.speedX *= -1;
+ changeColor();
+ }
+
+ if (logo.y + logo.height > canvas.height || logo.y < 0) {
+ logo.speedY *= -1;
+ changeColor();
+ }
+ }, 20);
+
+ function drawBackground() {
+ ctx.fillStyle = 'black';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ }
+
+ function drawLogo() {
+ ctx.drawImage(logoImg, logo.x, logo.y, logo.width, logo.height);
+ drawTrail();
+ }
+
+ function changeColor() {
+ const colors = ['red', 'blue', 'green', 'yellow', 'orange', 'purple'];
+ logo.color = colors[Math.floor(Math.random() * colors.length)];
+ }
+
+ function drawTrail() {
+ ctx.fillStyle = logo.trailColor;
+ for (let i = 0; i < logo.trailLength; i++) {
+ const x = logo.x - i * logo.width / logo.trailLength;
+ const y = logo.y - i * logo.height / logo.trailLength;
+ ctx.fillRect(x, y, logo.width / logo.trailLength, logo.height / logo.trailLength);
+ }
+ }
+
+ document.getElementById('addLogo').addEventListener('click', () => {
+ clearInterval(gameLoop);
+
+ canvas.width += 100;
+ canvas.height = 400;
+
+ logoImg.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAABCAIAAADPSFZ ASAADJklSNTbAAAACXBIWXMAAASUJAAAOjBgjfQAAA3NJREFCQ0BJQWxKAAMjPAeTm+//9/gNubzCw8D7f/z4L7+/v79++/z5syZGdOeYhAAAAAElFTkSuQmCC";
+ logo.width = 100;
+ logo.height = 100;
+
+ gameLoop = setInterval(() => {
+ drawBackground();
+ drawLogo();
+
+ logo.x += logo.speedX;
+ logo.y += logo.speedY;
+
+ if (logo.x + logo.width > canvas.width || logo.x < 0) {
+ logo.speedX *= -1;
+ changeColor();
+ }
+
+ if (logo.y + logo.height > canvas.height || logo.y < 0) {
+ logo.speedY *= -1;
+ changeColor();
+ }
+ }, 20);
+ });
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a03b18e65a8c/hermes3-8b.png b/data/artifacts/a03b18e65a8c/hermes3-8b.png
new file mode 100644
index 0000000..008f407
Binary files /dev/null and b/data/artifacts/a03b18e65a8c/hermes3-8b.png differ
diff --git a/data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.html b/data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..0957901
--- /dev/null
+++ b/data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.html
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Bouncing DVD Logo</title>
+<style>
+body { margin: 0; overflow: hidden; display: flex; justify-content: center; align-items: center; background-color: #000; }
+canvas { position: relative; }
+button { position: absolute; top: 10px; left: 10px; z-index: 2; }
+</style>
+</head>
+<body>
+<canvas id="canvas"></canvas>
+<button onclick="addLogo()">Add Logo</button>
+<script>
+const canvas = document.getElementById('canvas');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+class Logo {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.dx = 2 + Math.random() * 4;
+ this.dy = 2 + Math.random() * 4;
+ this.colors = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00'];
+ this.colorIndex = Math.floor(Math.random() * this.colors.length);
+ this.trail = [];
+ }
+
+ update() {
+ this.x += this.dx;
+ this.y += this.dy;
+
+ if (this.x + 100 > canvas.width || this.x < 0) {
+ this.dx *= -1;
+ this.colorIndex = (this.colorIndex + 1) % this.colors.length;
+ if (this.x + 100 >= canvas.width || this.x <= 0) this.explode();
+ }
+ if (this.y + 50 > canvas.height || this.y < 0) {
+ this.dy *= -1;
+ this.colorIndex = (this.colorIndex + 1) % this.colors.length;
+ if (this.y + 50 >= canvas.height || this.y <= 0) this.explode();
+ }
+
+ this.trail.push({ x: this.x, y: this.y });
+ while (this.trail.length > 20) {
+ this.trail.shift();
+ }
+ }
+
+ draw() {
+ ctx.fillStyle = this.colors[this.colorIndex];
+ ctx.fillRect(this.x, this.y, 100, 50);
+ for (let i = 0; i < this.trail.length - 1; i++) {
+ const currentTrail = this.trail[i];
+ const nextTrail = this.trail[i + 1];
+ ctx.beginPath();
+ ctx.strokeStyle = `rgba(${this.colors[this.colorIndex].slice(1)}, ${1 - (i / this.trail.length)})`;
+ ctx.lineWidth = 2;
+ ctx.moveTo(currentTrail.x, currentTrail.y);
+ ctx.lineTo(nextTrail.x, nextTrail.y);
+ ctx.stroke();
+ }
+ }
+
+ explode() {
+ for (let i = 0; i < 50; i++) {
+ particles.push(new Particle(this.x + 50, this.y + 25));
+ }
+ score++;
+ document.title = `Score: ${score}`;
+ }
+}
+
+class Particle {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.size = Math.random() * 3 + 1;
+ this.color = `hsl(${Math.random() * 360}, 100%, 50%)`;
+ this.lifeSpan = Math.random() * 20 + 10;
+ this.angle = Math.random() * Math.PI * 2;
+ this.speed = (Math.random() * 4) / 2;
+ }
+
+ update() {
+ this.x += Math.cos(this.angle) * this.speed;
+ this.y += Math.sin(this.angle) * this.speed;
+ this.lifeSpan--;
+ }
+
+ draw() {
+ ctx.fillStyle = this.color;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ ctx.fill();
+ }
+}
+
+let logos = [new Logo(50, 50)];
+let particles = [];
+let score = 0;
+
+function animate() {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = logos.length - 1; i >= 0; i--) {
+ logos[i].update();
+ logos[i].draw();
+ if (logos[i].trail.length === 0) logos.splice(i, 1);
+ }
+
+ for (let i = particles.length - 1; i >= 0; i--) {
+ particles[i].update();
+ particles[i].draw();
+ if (particles[i].lifeSpan <= 0) particles.splice(i, 1);
+ }
+
+ requestAnimationFrame(animate);
+}
+
+function addLogo() {
+ logos.push(new Logo(Math.random() * (canvas.width - 100), Math.random() * (canvas.height - 50)));
+}
+
+animate();
+
+window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.png b/data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..871fbb7
Binary files /dev/null and b/data/artifacts/a03b18e65a8c/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/a03b18e65a8c/qwen25-7b.html b/data/artifacts/a03b18e65a8c/qwen25-7b.html
new file mode 100644
index 0000000..f656e3d
--- /dev/null
+++ b/data/artifacts/a03b18e65a8c/qwen25-7b.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Bouncing DVD Logo</title>
+ <style>
+ body { margin: 0; overflow: hidden; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #282c34; }
+ canvas { border: 1px solid white; }
+ </style>
+</head>
+<body>
+<canvas id="canvas"></canvas>
+<script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ class Logo {
+ constructor(x, y, dx, dy, color) {
+ this.x = x;
+ this.y = y;
+ this.dx = dx;
+ this.dy = dy;
+ this.color = color;
+ this.trail = [];
+ }
+
+ update() {
+ if (this.x + 20 > canvas.width || this.x < 0) {
+ this.dx *= -1;
+ this.color = `hsl(${(Math.random() * 360).toFixed()}, 50%, 50%)`;
+ }
+ if (this.y + 20 > canvas.height || this.y < 0) {
+ this.dy *= -1;
+ this.color = `hsl(${(Math.random() * 360).toFixed()}, 50%, 50%)`;
+ }
+ this.x += this.dx;
+ this.y += this.dy;
+
+ if (this.trail.length > 50) {
+ this.trail.shift();
+ }
+
+ this.trail.push({ x: this.x, y: this.y });
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x + 10, this.y + 10, 10, 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+
+ ctx.lineWidth = 1.5;
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
+ for (const point of this.trail) {
+ ctx.fillRect(point.x - 1, point.y - 1, 2, 2);
+ }
+ }
+
+ checkWallHit() {
+ if (this.x + 20 > canvas.width || this.x < 0) {
+ return true;
+ }
+ if (this.y + 20 > canvas.height || this.y < 0) {
+ return true;
+ }
+ return false;
+ }
+
+ handleWallHit() {
+ const x = this.x + 10;
+ const y = this.y + 10;
+ ctx.beginPath();
+ ctx.arc(x, y, 5, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
+ ctx.fill();
+
+ for (let i = -3; i <= 3; i++) {
+ const burstX = x + i;
+ const burstY = y + i;
+ ctx.beginPath();
+ ctx.arc(burstX, burstY, 4, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
+ ctx.fill();
+ }
+
+ const counter = document.createElement('div');
+ counter.style.position = 'absolute';
+ counter.style.zIndex = '10';
+ counter.style.color = 'white';
+ counter.style.textAlign = 'center';
+ counter.innerText = `Bounce!`;
+ counter.style.fontSize = '24px';
+ canvas.appendChild(counter);
+ setTimeout(() => {
+ counter.remove();
+ }, 500);
+ }
+ }
+
+ const logo = new Logo(canvas.width / 2, canvas.height / 2, 3 + Math.random() * 2, 3 + Math.random() * 2, 'hsl(' + (Math.random() * 360).toFixed() + ', 50%, 50%)');
+
+ function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ logo.update();
+ logo.draw();
+
+ if (logo.checkWallHit()) {
+ logo.handleWallHit();
+ }
+
+ requestAnimationFrame(animate);
+ }
+
+ document.addEventListener('keydown', e => {
+ if (e.key === 'a') {
+ const addLogo = new Logo(canvas.width / 2, canvas.height / 2, 3 + Math.random() * 2, 3 + Math.random() * 2, 'hsl(' + (Math.random() * 360).toFixed() + ', 50%, 50%)');
+ addLogo.update();
+ logo.trail.push(...addLogo.trail);
+ }
+ });
+
+ animate();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a03b18e65a8c/qwen25-7b.png b/data/artifacts/a03b18e65a8c/qwen25-7b.png
new file mode 100644
index 0000000..a2c7379
Binary files /dev/null and b/data/artifacts/a03b18e65a8c/qwen25-7b.png differ
diff --git a/data/artifacts/a03b18e65a8c/qwen3-14b.html b/data/artifacts/a03b18e65a8c/qwen3-14b.html
new file mode 100644
index 0000000..2497c49
--- /dev/null
+++ b/data/artifacts/a03b18e65a8c/qwen3-14b.html
@@ -0,0 +1,173 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Bouncing DVD Logo</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #000;
+ height: 100%;
+ width: 100%;
+ }
+ canvas {
+ display: block;
+ }
+ #addLogo {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ padding: 10px 20px;
+ font-family: sans-serif;
+ font-size: 16px;
+ background: #fff;
+ color: #000;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ z-index: 10;
+ }
+ </style>
+</head>
+<body>
+ <button id="addLogo">Add Logo</button>
+ <canvas id="canvas"></canvas>
+ <script>
+ const canvas = document.getElementById("canvas");
+ const ctx = canvas.getContext("2d");
+ let width = canvas.width = window.innerWidth;
+ let height = canvas.height = window.innerHeight;
+ window.addEventListener("resize", () => {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ });
+
+ const colors = ["#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF", "#FFA500", "#800080"];
+ let particles = [];
+ let logoCount = 0;
+
+ function getRandomColor() {
+ return colors[Math.floor(Math.random() * colors.length)];
+ }
+
+ function createLogo(x, y) {
+ const size = 100;
+ const speed = { x: (Math.random() - 0.5) * 4, y: (Math.random() - 0.5) * 4 };
+ const color = getRandomColor();
+ const trail = [];
+ let bounceCount = 0;
+
+ function draw() {
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.fillStyle = color;
+ ctx.shadowColor = color;
+ ctx.shadowBlur = 20;
+ ctx.font = "bold 48px Arial";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "middle";
+ ctx.fillText("DVD", 0, 0);
+ ctx.restore();
+
+ // Trail effect
+ trail.push({ x, y });
+ if (trail.length > 10) trail.shift();
+ ctx.shadowColor = "transparent";
+ for (let i = 0; i < trail.length; i++) {
+ const opacity = 1 - i / trail.length;
+ ctx.globalAlpha = opacity;
+ ctx.fillStyle = color;
+ ctx.font = "bold 48px Arial";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "middle";
+ ctx.fillText("DVD", trail[i].x, trail[i].y);
+ }
+ ctx.globalAlpha = 1;
+ }
+
+ function update() {
+ x += speed.x;
+ y += speed.y;
+
+ // Bounce off walls
+ if (x < size / 2 || x > width - size / 2) {
+ speed.x *= -1;
+ bounceCount++;
+ if (x < size / 2) x = size / 2;
+ else x = width - size / 2;
+ color = getRandomColor();
+ }
+ if (y < size / 2 || y > height - size / 2) {
+ speed.y *= -1;
+ bounceCount++;
+ if (y < size / 2) y = size / 2;
+ else y = height - size / 2;
+ color = getRandomColor();
+ }
+
+ // Corner detection
+ const cornerSize = 20;
+ if (
+ (x < cornerSize && y < cornerSize) ||
+ (x > width - cornerSize && y < cornerSize) ||
+ (x < cornerSize && y > height - cornerSize) ||
+ (x > width - cornerSize && y > height - cornerSize)
+ ) {
+ // Particle burst
+ for (let i = 0; i < 50; i++) {
+ particles.push({
+ x: x + (Math.random() - 0.5) * 50,
+ y: y + (Math.random() - 0.5) * 50,
+ vx: (Math.random() - 0.5) * 10,
+ vy: (Math.random() - 0.5) * 10,
+ size: Math.random() * 4 + 2,
+ life: 100,
+ color: getRandomColor()
+ });
+ }
+ bounceCount++;
+ }
+
+ // Update trail
+ trail.push({ x, y });
+ if (trail.length > 10) trail.shift();
+
+ draw();
+ }
+
+ return { x, y, speed, color, trail, update, bounceCount };
+ }
+
+ const logos = [createLogo(100, 100)];
+ const addLogoButton = document.getElementById("addLogo");
+ addLogoButton.addEventListener("click", () => {
+ logos.push(createLogo(Math.random() * (width - 100), Math.random() * (height - 100)));
+ });
+
+ function drawParticles() {
+ for (let i = particles.length - 1; i >= 0; i--) {
+ const p = particles[i];
+ p.x += p.vx;
+ p.y += p.vy;
+ p.life--;
+ ctx.beginPath();
+ ctx.fillStyle = p.color;
+ ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
+ ctx.fill();
+ if (p.life <= 0) particles.splice(i, 1);
+ }
+ }
+
+ function animate() {
+ ctx.clearRect(0, 0, width, height);
+ logos.forEach(logo => logo.update());
+ drawParticles();
+ requestAnimationFrame(animate);
+ }
+
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a03b18e65a8c/qwen3-14b.png b/data/artifacts/a03b18e65a8c/qwen3-14b.png
new file mode 100644
index 0000000..fcc7104
Binary files /dev/null and b/data/artifacts/a03b18e65a8c/qwen3-14b.png differ
diff --git a/data/artifacts/a7640ab6a2fa/claude-code.html b/data/artifacts/a7640ab6a2fa/claude-code.html
new file mode 100644
index 0000000..3e67194
--- /dev/null
+++ b/data/artifacts/a7640ab6a2fa/claude-code.html
@@ -0,0 +1,333 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Spirograph Studio</title>
+<style>
+ :root{
+ --bg:#0d0f1a; --panel:#161a2e; --panel2:#1e2340;
+ --ink:#e8ecff; --muted:#8b91b8; --accent:#5eead4; --accent2:#a78bfa;
+ --line:#2a3055;
+ }
+ *{box-sizing:border-box}
+ html,body{height:100%}
+ body{
+ margin:0; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
+ background:radial-gradient(1200px 800px at 70% -10%,#1b2145 0%,var(--bg) 55%);
+ color:var(--ink); display:flex; min-height:100%; overflow:hidden;
+ }
+ .wrap{display:flex; width:100%; height:100vh}
+ .stage{
+ flex:1; display:flex; align-items:center; justify-content:center;
+ position:relative; padding:24px;
+ }
+ #canvas{
+ background:#07080f;
+ border-radius:16px;
+ box-shadow:0 20px 60px rgba(0,0,0,.55), inset 0 0 0 1px rgba(255,255,255,.04);
+ max-width:100%; max-height:100%;
+ touch-action:none;
+ }
+ .panel{
+ width:320px; flex-shrink:0; background:linear-gradient(180deg,var(--panel),var(--panel2));
+ border-left:1px solid var(--line);
+ padding:22px 22px 26px; display:flex; flex-direction:column; gap:20px;
+ overflow-y:auto;
+ }
+ .brand{display:flex; align-items:center; gap:11px}
+ .brand .dot{
+ width:30px;height:30px;border-radius:9px;
+ background:conic-gradient(from 0deg,var(--accent),var(--accent2),#f472b6,var(--accent));
+ box-shadow:0 0 18px rgba(94,234,212,.4);
+ }
+ .brand h1{font-size:17px; margin:0; letter-spacing:.4px; font-weight:650}
+ .brand span{font-size:11px; color:var(--muted); display:block; margin-top:1px; letter-spacing:.5px}
+
+ .ctrl{display:flex; flex-direction:column; gap:8px}
+ .ctrl label{display:flex; justify-content:space-between; align-items:baseline; font-size:12.5px; color:var(--muted); letter-spacing:.3px}
+ .ctrl label b{color:var(--ink); font-weight:600; font-variant-numeric:tabular-nums; font-size:13px}
+ input[type=range]{
+ -webkit-appearance:none; appearance:none; width:100%; height:6px; border-radius:6px;
+ background:linear-gradient(90deg,var(--accent),var(--accent2)); outline:none; cursor:pointer;
+ }
+ input[type=range]::-webkit-slider-thumb{
+ -webkit-appearance:none; width:18px;height:18px;border-radius:50%;
+ background:#fff; border:3px solid var(--accent); box-shadow:0 2px 8px rgba(0,0,0,.5); cursor:pointer;
+ }
+ input[type=range]::-moz-range-thumb{
+ width:16px;height:16px;border-radius:50%; background:#fff; border:3px solid var(--accent); cursor:pointer;
+ }
+ .row{display:flex; gap:10px}
+ .toggle{
+ display:flex; align-items:center; justify-content:space-between; gap:10px;
+ font-size:12.5px; color:var(--muted); background:rgba(255,255,255,.03);
+ padding:10px 12px; border-radius:10px; border:1px solid var(--line);
+ }
+ .switch{position:relative; width:42px; height:23px; flex-shrink:0}
+ .switch input{opacity:0; width:0; height:0}
+ .slider-sw{
+ position:absolute; inset:0; background:#3a4066; border-radius:23px; transition:.2s; cursor:pointer;
+ }
+ .slider-sw:before{
+ content:""; position:absolute; height:17px; width:17px; left:3px; top:3px;
+ background:#fff; border-radius:50%; transition:.2s;
+ }
+ .switch input:checked + .slider-sw{background:linear-gradient(90deg,var(--accent),var(--accent2))}
+ .switch input:checked + .slider-sw:before{transform:translateX(19px)}
+
+ button{
+ flex:1; font:inherit; font-size:13px; font-weight:600; letter-spacing:.3px;
+ padding:12px 10px; border-radius:11px; border:1px solid var(--line);
+ background:rgba(255,255,255,.04); color:var(--ink); cursor:pointer; transition:.15s;
+ }
+ button:hover{background:rgba(255,255,255,.09); transform:translateY(-1px)}
+ button:active{transform:translateY(0)}
+ button.primary{
+ background:linear-gradient(90deg,var(--accent),var(--accent2)); color:#0b0d18; border:none;
+ }
+ button.primary:hover{filter:brightness(1.08)}
+ .foot{margin-top:auto; font-size:11px; color:var(--muted); line-height:1.5; letter-spacing:.2px}
+ .hint{font-size:11px; color:var(--muted); margin-top:-6px}
+ @media (max-width:760px){
+ body{overflow:auto}
+ .wrap{flex-direction:column; height:auto}
+ .panel{width:100%; border-left:none; border-top:1px solid var(--line)}
+ .stage{padding:16px; min-height:60vh}
+ }
+</style>
+</head>
+<body>
+<div class="wrap">
+ <div class="stage">
+ <canvas id="canvas" width="760" height="760"></canvas>
+ </div>
+
+ <aside class="panel">
+ <div class="brand">
+ <div class="dot"></div>
+ <div>
+ <h1>Spirograph Studio</h1>
+ <span>HYPOTROCHOID PLOTTER</span>
+ </div>
+ </div>
+
+ <div class="ctrl">
+ <label>Fixed gear radius <span>R</span> <b id="vR">140</b></label>
+ <input type="range" id="R" min="40" max="220" value="140" step="1">
+ </div>
+
+ <div class="ctrl">
+ <label>Rolling gear radius <span>r</span> <b id="vr">53</b></label>
+ <input type="range" id="r" min="8" max="150" value="53" step="1">
+ </div>
+
+ <div class="ctrl">
+ <label>Pen offset <span>d</span> <b id="vd">78</b></label>
+ <input type="range" id="d" min="0" max="150" value="78" step="1">
+ </div>
+
+ <div class="ctrl">
+ <label>Speed <b id="vs">6</b></label>
+ <input type="range" id="speed" min="1" max="24" value="6" step="1">
+ </div>
+
+ <div class="ctrl">
+ <label>Line width <b id="vw">1.6</b></label>
+ <input type="range" id="lw" min="1" max="6" value="1.6" step="0.1">
+ </div>
+
+ <div class="toggle">
+ <span>Color cycling</span>
+ <label class="switch"><input type="checkbox" id="cycle" checked><span class="slider-sw"></span></label>
+ </div>
+
+ <div class="toggle">
+ <span>Show gears</span>
+ <label class="switch"><input type="checkbox" id="gears" checked><span class="slider-sw"></span></label>
+ </div>
+
+ <div class="row">
+ <button id="clear">Clear</button>
+ <button id="save" class="primary">Save PNG</button>
+ </div>
+ <div class="hint">Adjust any slider and the curve redraws live. It draws until the pattern closes, then loops.</div>
+
+ <div class="foot">
+ Curve: x = (R−r)·cos t + d·cos((R−r)/r·t)<br>
+ y = (R−r)·sin t − d·sin((R−r)/r·t)
+ </div>
+ </aside>
+</div>
+
+<script>
+(function(){
+ "use strict";
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const W = canvas.width, H = canvas.height;
+ const cx = W/2, cy = H/2;
+
+ // Controls
+ const el = id => document.getElementById(id);
+ const R = el('R'), r = el('r'), d = el('d'), speed = el('speed'), lw = el('lw');
+ const cycle = el('cycle'), gears = el('gears');
+ const vR=el('vR'), vr=el('vr'), vd=el('vd'), vs=el('vs'), vw=el('vw');
+
+ // Drawing state
+ let t = 0; // parameter
+ let hue = Math.random()*360;
+ let lastPt = null; // last plotted point on the persistent trail
+ let period = 0; // t value at which the curve closes
+ let running = true;
+
+ // Offscreen trail layer (persists between frames; gear overlay is drawn fresh each frame)
+ const trail = document.createElement('canvas');
+ trail.width = W; trail.height = H;
+ const tctx = trail.getContext('2d');
+
+ function gcd(a,b){ a=Math.round(a); b=Math.round(b); while(b){ [a,b]=[b,a%b]; } return a||1; }
+
+ function computePeriod(){
+ const rr = Math.max(1, parseInt(r.value,10));
+ const RR = parseInt(R.value,10);
+ // full pattern closes after r/gcd(R,r) revolutions of the driving angle
+ const revs = rr / gcd(RR, rr);
+ period = 2*Math.PI*revs;
+ }
+
+ function point(tt){
+ const RR = parseInt(R.value,10);
+ const rr = Math.max(1, parseInt(r.value,10));
+ const dd = parseInt(d.value,10);
+ const k = RR - rr;
+ const ratio = k / rr;
+ return {
+ x: cx + k*Math.cos(tt) + dd*Math.cos(ratio*tt),
+ y: cy + k*Math.sin(tt) - dd*Math.sin(ratio*tt)
+ };
+ }
+
+ function resetTrail(){
+ tctx.clearRect(0,0,W,H);
+ t = 0;
+ lastPt = point(0);
+ computePeriod();
+ }
+
+ function restart(){
+ // called when a shape parameter changes: fresh curve
+ hue = (hue + 47) % 360;
+ resetTrail();
+ render(); // paint immediately so change is visible even if paused between frames
+ }
+
+ // Draw the rolling-gear schematic for the current t (overlay only)
+ function drawGears(octx, tt){
+ const RR = parseInt(R.value,10);
+ const rr = Math.max(1, parseInt(r.value,10));
+ const dd = parseInt(d.value,10);
+ const k = RR - rr;
+ // fixed gear
+ octx.save();
+ octx.strokeStyle = 'rgba(255,255,255,.16)';
+ octx.lineWidth = 1;
+ octx.beginPath(); octx.arc(cx, cy, RR, 0, Math.PI*2); octx.stroke();
+ // rolling gear center
+ const gx = cx + k*Math.cos(tt);
+ const gy = cy + k*Math.sin(tt);
+ octx.strokeStyle = 'rgba(167,139,250,.55)';
+ octx.beginPath(); octx.arc(gx, gy, rr, 0, Math.PI*2); octx.stroke();
+ // pen position (= current curve point)
+ const p = point(tt);
+ octx.strokeStyle = 'rgba(94,234,212,.5)';
+ octx.beginPath(); octx.moveTo(gx,gy); octx.lineTo(p.x,p.y); octx.stroke();
+ octx.fillStyle = '#5eead4';
+ octx.beginPath(); octx.arc(p.x,p.y,3.2,0,Math.PI*2); octx.fill();
+ octx.fillStyle = 'rgba(167,139,250,.9)';
+ octx.beginPath(); octx.arc(gx,gy,2.5,0,Math.PI*2); octx.fill();
+ octx.restore();
+ }
+
+ function render(){
+ ctx.clearRect(0,0,W,H);
+ ctx.drawImage(trail,0,0);
+ if(gears.checked && running) drawGears(ctx, t);
+ }
+
+ function frame(){
+ if(running){
+ const steps = parseInt(speed.value,10);
+ const width = parseFloat(lw.value);
+ const dt = 0.012; // base angular step per micro-segment
+ tctx.lineCap = 'round';
+ tctx.lineJoin = 'round';
+ tctx.lineWidth = width;
+
+ for(let i=0;i<steps;i++){
+ const nt = t + dt;
+ const p = point(nt);
+ if(cycle.checked){
+ hue = (hue + 0.35) % 360;
+ tctx.strokeStyle = 'hsl('+hue.toFixed(1)+',85%,62%)';
+ } else {
+ tctx.strokeStyle = 'hsl('+hue.toFixed(1)+',85%,62%)';
+ }
+ tctx.beginPath();
+ tctx.moveTo(lastPt.x,lastPt.y);
+ tctx.lineTo(p.x,p.y);
+ tctx.stroke();
+ lastPt = p;
+ t = nt;
+ if(t >= period){
+ // pattern complete: keep it, then start a fresh overlaid pass with a new base hue
+ t = 0;
+ lastPt = point(0);
+ hue = (hue + 63) % 360;
+ }
+ }
+ }
+ render();
+ requestAnimationFrame(frame);
+ }
+
+ // Wire up value labels + change handlers
+ function bind(input, label, fmt){
+ const upd = ()=> label.textContent = fmt ? fmt(input.value) : input.value;
+ input.addEventListener('input', upd); upd();
+ return upd;
+ }
+ bind(R, vR);
+ bind(r, vr);
+ bind(d, vd);
+ bind(speed, vs);
+ bind(lw, vw);
+
+ // Shape-changing sliders restart the curve
+ [R,r,d].forEach(inp => inp.addEventListener('input', restart));
+
+ el('clear').addEventListener('click', ()=>{
+ restart();
+ });
+
+ el('save').addEventListener('click', ()=>{
+ // Composite onto a dark background so the PNG isn't transparent
+ const out = document.createElement('canvas');
+ out.width = W; out.height = H;
+ const octx = out.getContext('2d');
+ octx.fillStyle = '#07080f';
+ octx.fillRect(0,0,W,H);
+ octx.drawImage(trail,0,0);
+ const link = document.createElement('a');
+ link.download = 'spirograph-' + Date.now() + '.png';
+ link.href = out.toDataURL('image/png');
+ link.click();
+ });
+
+ // Init
+ resetTrail();
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a7640ab6a2fa/claude-code.png b/data/artifacts/a7640ab6a2fa/claude-code.png
new file mode 100644
index 0000000..b4e78d4
Binary files /dev/null and b/data/artifacts/a7640ab6a2fa/claude-code.png differ
diff --git a/data/artifacts/a7640ab6a2fa/gemma3-12b.html b/data/artifacts/a7640ab6a2fa/gemma3-12b.html
new file mode 100644
index 0000000..2c535ec
--- /dev/null
+++ b/data/artifacts/a7640ab6a2fa/gemma3-12b.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Spirograph Animator</title>
+<style>
+body { font-family: sans-serif; }
+#controls { margin-bottom: 20px; }
+label { display: inline-block; width: 80px; text-align: right; margin-right: 10px; }
+input[type="range"] { width: 150px; }
+button { margin-left: 10px; }
+#canvasContainer { position: relative; }
+#spirographCanvas { border: 1px solid black; }
+</style>
+</head>
+<body>
+
+<div id="controls">
+ <label for="radius1">Radius 1:</label>
+ <input type="range" id="radius1" min="5" max="80" value="40"><br>
+
+ <label for="radius2">Radius 2:</label>
+ <input type="range" id="radius2" min="5" max="80" value="20"><br>
+
+ <label for="offset">Offset:</label>
+ <input type="range" id="offset" min="-30" max="30" value="10"><br>
+
+ <button onclick="clearCanvas()">Clear</button>
+ <button onclick="savePNG()">Save PNG</button>
+</div>
+
+<div id="canvasContainer">
+ <canvas id="spirographCanvas" width="500" height="500"></canvas>
+</div>
+
+
+<script>
+const canvas = document.getElementById('spirographCanvas');
+const ctx = canvas.getContext('2d');
+const radius1Input = document.getElementById('radius1');
+const radius2Input = document.getElementById('radius2');
+const offsetInput = document.getElementById('offset');
+
+let radius1 = parseInt(radius1Input.value);
+let radius2 = parseInt(radius2Input.value);
+let offset = parseInt(offsetInput.value);
+
+function drawSpirograph() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ const centerX = canvas.width / 2;
+ const centerY = canvas.height / 2;
+
+ ctx.strokeStyle = `hsl(${Math.random() * 360}, 100%, 50%)`; // Cycling color
+ ctx.lineWidth = 2;
+ ctx.lineCap = 'round';
+
+ ctx.beginPath();
+ let x = centerX + (radius1 - radius2) * Math.cos(0);
+ let y = centerY + radius1 * Math.sin(0);
+ ctx.moveTo(x, y);
+
+ for (let t = 0; t < 1000; t++) {
+ const angle = t / 100; // Adjust for smoother/faster drawing
+ const x = centerX + (radius1 - radius2) * Math.cos(angle);
+ const y = centerY + radius1 * Math.sin(angle);
+
+ ctx.lineTo(x, y);
+ }
+
+ ctx.stroke();
+}
+
+
+function updateValues() {
+ radius1 = parseInt(radius1Input.value);
+ radius2 = parseInt(radius2Input.value);
+ offset = parseInt(offsetInput.value);
+ drawSpirograph();
+}
+
+radius1Input.addEventListener('input', updateValues);
+radius2Input.addEventListener('input', updateValues);
+offsetInput.addEventListener('input', updateValues);
+
+
+function clearCanvas() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+}
+
+function savePNG() {
+ const image = canvas.toDataURL("image/png");
+ const link = document.createElement('a');
+ link.download = 'spirograph.png';
+ link.href = image;
+ link.click();
+}
+
+
+drawSpirograph(); // Initial draw
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a7640ab6a2fa/gemma3-12b.png b/data/artifacts/a7640ab6a2fa/gemma3-12b.png
new file mode 100644
index 0000000..6f7b8a2
Binary files /dev/null and b/data/artifacts/a7640ab6a2fa/gemma3-12b.png differ
diff --git a/data/artifacts/a7640ab6a2fa/hermes3-8b.html b/data/artifacts/a7640ab6a2fa/hermes3-8b.html
new file mode 100644
index 0000000..e8574fc
--- /dev/null
+++ b/data/artifacts/a7640ab6a2fa/hermes3-8b.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Animated Spirograph</title>
+ <style>
+ body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
+ canvas { background: #f0f0f0; }
+ input, button { margin: 5px; }
+ </style>
+</head>
+<body>
+ <canvas id="spirograph" width="800" height="600"></canvas>
+ <input type="range" id="gear1" min="1" max="300">
+ <input type="range" id="gear2" min="1" max="300">
+ <button id="clear">Clear</button>
+ <button id="save">Save PNG</button>
+
+ <script>
+ const canvas = document.getElementById('spirograph');
+ const ctx = canvas.getContext('2d');
+
+ let gear1 = 100;
+ let gear2 = 200;
+
+ function drawSpiro() {
+ ctx.strokeStyle = `hsl(${Math.floor(gear1 * Math.sin(gear2++) / 4) % 360}, 100%, 50%)`;
+ ctx.lineWidth = 2;
+ ctx.lineCap = 'round';
+
+ ctx.beginPath();
+ ctx.moveTo(canvas.width / 2, canvas.height / 2);
+ for (let a = 0; a < 2 * Math.PI; a += 0.01) {
+ const x = (canvas.width / 2 + gear1 * Math.cos(gear2)) * Math.cos(a) - (canvas.height / 2 + gear1 * Math.sin(gear2)) * Math.sin(a);
+ const y = (canvas.height / 2 + gear1 * Math.sin(gear2)) * Math.cos(a) + (canvas.width / 2 + gear1 * Math.cos(gear2)) * Math.sin(a);
+
+ ctx.lineTo(x, y);
+ }
+
+ ctx.stroke();
+ }
+
+ function clearCanvas() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ }
+
+ document.getElementById('gear1').addEventListener('input', () => { gear1 = parseInt(document.getElementById('gear1').value); });
+ document.getElementById('gear2').addEventListener('input', () => { gear2 = parseInt(document.getElementById('gear2').value); });
+
+ document.getElementById('clear').addEventListener('click', clearCanvas);
+ document.getElementById('save').addEventListener('click', () => {
+ const link = document.createElement('a');
+ link.href = canvas.toDataURL();
+ link.download = 'spirograph.png';
+ link.click();
+ });
+
+ setInterval(drawSpiro, 10);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a7640ab6a2fa/hermes3-8b.png b/data/artifacts/a7640ab6a2fa/hermes3-8b.png
new file mode 100644
index 0000000..f85b1cc
Binary files /dev/null and b/data/artifacts/a7640ab6a2fa/hermes3-8b.png differ
diff --git a/data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.html b/data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..cbde9aa
--- /dev/null
+++ b/data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Spirograph Drawing Tool</title>
+<style>
+body { display: flex; justify-content: center; align-items: center; flex-direction: column; height: 100vh; background-color: #f0f0f0; margin: 0; }
+canvas { border: 1px solid #ccc; background-color: white; }
+.controls { margin-top: 20px; display: flex; flex-direction: column; align-items: center; }
+input[type="range"] { width: 300px; margin: 5px 0; }
+button { padding: 10px 20px; margin: 5px; cursor: pointer; background-color: #007BFF; color: white; border: none; border-radius: 5px; }
+button:hover { background-color: #0056b3; }
+</style>
+</head>
+<body>
+<canvas id="spirograph" width="800" height="800"></canvas>
+<div class="controls">
+<label>R1 (Outer Radius): <span id="r1Value">200</span></label>
+<input type="range" id="r1" min="50" max="400" value="200" oninput="updateR1(this.value)">
+<label>R2 (Inner Radius): <span id="r2Value">80</span></label>
+<input type="range" id="r2" min="10" max="200" value="80" oninput="updateR2(this.value)">
+<label>Offset: <span id="offsetValue">50</span></label>
+<input type="range" id="offset" min="10" max="200" value="50" oninput="updateOffset(this.value)">
+<button onclick="clearCanvas()">Clear</button>
+<button onclick="saveImage()">Save as PNG</button>
+</div>
+<script>
+const canvas = document.getElementById('spirograph');
+const ctx = canvas.getContext('2d');
+let R1 = 200, R2 = 80, offset = 50;
+let angle = 0;
+let hue = 0;
+
+function drawSpirograph() {
+ const x = (R1 - R2) * Math.cos(angle) + offset * Math.cos(((R1 - R2) / R2) * angle);
+ const y = (R1 - R2) * Math.sin(angle) - offset * Math.sin(((R1 - R2) / R2) * angle);
+
+ ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
+ hue = (hue + 1) % 360;
+ ctx.beginPath();
+ ctx.arc(x + canvas.width/2, y + canvas.height/2, 2, 0, Math.PI * 2);
+ ctx.stroke();
+
+ angle += 0.05;
+}
+
+function animate() {
+ drawSpirograph();
+ requestAnimationFrame(animate);
+}
+
+document.getElementById('r1').addEventListener('input', (e) => {
+ R1 = parseInt(e.target.value);
+ document.getElementById('r1Value').textContent = e.target.value;
+});
+document.getElementById('r2').addEventListener('input', (e) => {
+ R2 = parseInt(e.target.value);
+ document.getElementById('r2Value').textContent = e.target.value;
+});
+document.getElementById('offset').addEventListener('input', (e) => {
+ offset = parseInt(e.target.value);
+ document.getElementById('offsetValue').textContent = e.target.value;
+});
+
+function updateR1(value) { R1 = value; }
+function updateR2(value) { R2 = value; }
+function updateOffset(value) { offset = value; }
+
+function clearCanvas() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ angle = 0;
+}
+
+function saveImage() {
+ const link = document.createElement('a');
+ link.href = canvas.toDataURL();
+ link.download = 'spirograph.png';
+ link.click();
+}
+
+animate();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.png b/data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..7542013
Binary files /dev/null and b/data/artifacts/a7640ab6a2fa/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/a7640ab6a2fa/qwen25-7b.html b/data/artifacts/a7640ab6a2fa/qwen25-7b.html
new file mode 100644
index 0000000..af9c9f4
--- /dev/null
+++ b/data/artifacts/a7640ab6a2fa/qwen25-7b.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Spirograph Tool</title>
+ <style>
+ body { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f0f0; }
+ canvas { border: 1px solid black; cursor: pointer; }
+ div { margin-top: 20px; }
+ </style>
+</head>
+<body>
+ <canvas id="spirograph" width="600" height="600"></canvas>
+ <div>
+ <label for="r1">Gear Radius 1:</label><input type="range" id="r1" min="50" max="200" value="75">
+ <label for="r2">Gear Radius 2:</label><input type="range" id="r2" min="50" max="200" value="100">
+ <label for="offset">Pen Offset:</label><input type="range" id="offset" min="0" max="50" value="25">
+ </div>
+ <button onclick="clearCanvas()">Clear</button>
+ <a href="" id="downloadLink" download="spirograph.png">Save PNG</a>
+ <script>
+ const canvas = document.getElementById('spirograph');
+ const ctx = canvas.getContext('2d');
+ let r1 = 75, r2 = 100, offset = 25;
+ let colorIndex = 0;
+
+ function drawSpirograph() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ const t = Math.PI * 2 / (r1 - r2) * 0.75; // Initial angle
+ const x = (r1 - r2) * Math.cos(t) + offset * Math.cos((r1 - r2) / r2 * t);
+ const y = (r1 - r2) * Math.sin(t) - offset * Math.sin((r1 - r2) / r2 * t);
+
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ for (let i = 0; i < 360; i += 1) {
+ const angle = Math.PI * 2 * i / 180;
+ const x2 = (r1 - r2) * Math.cos(angle * t) + offset * Math.cos((r1 - r2) / r2 * angle * t);
+ const y2 = (r1 - r2) * Math.sin(angle * t) - offset * Math.sin((r1 - r2) / r2 * angle * t);
+ ctx.lineTo(x2, y2);
+ [x, y] = [x2, y2];
+ }
+ ctx.strokeStyle = `hsl(${colorIndex}, 100%, 50%)`;
+ colorIndex = (colorIndex + 1) % 360;
+ ctx.stroke();
+ }
+
+ function clearCanvas() {
+ drawSpirograph();
+ }
+
+ document.getElementById('r1').oninput = () => { r1 = parseInt(this.value); drawSpirograph(); };
+ document.getElementById('r2').oninput = () => { r2 = parseInt(this.value); drawSpirograph(); };
+ document.getElementById('offset').oninput = () => { offset = parseInt(this.value); drawSpirograph(); };
+
+ drawSpirograph();
+
+ function downloadCanvas() {
+ const link = document.getElementById('downloadLink');
+ const canvasDataUrl = canvas.toDataURL();
+ link.href = canvasDataUrl;
+ }
+
+ setInterval(downloadCanvas, 1000);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a7640ab6a2fa/qwen25-7b.png b/data/artifacts/a7640ab6a2fa/qwen25-7b.png
new file mode 100644
index 0000000..f795818
Binary files /dev/null and b/data/artifacts/a7640ab6a2fa/qwen25-7b.png differ
diff --git a/data/artifacts/a7640ab6a2fa/qwen3-14b.html b/data/artifacts/a7640ab6a2fa/qwen3-14b.html
new file mode 100644
index 0000000..f56bc9f
--- /dev/null
+++ b/data/artifacts/a7640ab6a2fa/qwen3-14b.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Animated Spirograph</title>
+ <style>
+ body {
+ margin: 0;
+ background: #111;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ font-family: sans-serif;
+ color: #fff;
+ }
+ h1 {
+ margin-top: 20px;
+ }
+ canvas {
+ background: #222;
+ border: 2px solid #555;
+ margin-top: 20px;
+ }
+ .controls {
+ margin-top: 10px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ justify-content: center;
+ }
+ .slider-group {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ }
+ .slider-group label {
+ margin-bottom: 4px;
+ }
+ input[type="range"] {
+ width: 200px;
+ }
+ .buttons {
+ margin-top: 10px;
+ }
+ button {
+ padding: 8px 16px;
+ margin: 0 5px;
+ font-size: 14px;
+ cursor: pointer;
+ }
+ </style>
+</head>
+<body>
+ <h1>Animated Spirograph</h1>
+ <div class="controls">
+ <div class="slider-group">
+ <label for="R">Outer Gear Radius (R):</label>
+ <input type="range" id="R" min="50" max="300" value="100" />
+ </div>
+ <div class="slider-group">
+ <label for="r">Inner Gear Radius (r):</label>
+ <input type="range" id="r" min="10" max="150" value="50" />
+ </div>
+ <div class="slider-group">
+ <label for="d">Pen Offset (d):</label>
+ <input type="range" id="d" min="1" max="100" value="40" />
+ </div>
+ </div>
+ <canvas id="canvas" width="600" height="400"></canvas>
+ <div class="buttons">
+ <button id="clear">Clear</button>
+ <button id="save">Save as PNG</button>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const RSlider = document.getElementById('R');
+ const rSlider = document.getElementById('r');
+ const dSlider = document.getElementById('d');
+ const clearBtn = document.getElementById('clear');
+ const saveBtn = document.getElementById('save');
+
+ let R = parseFloat(RSlider.value);
+ let r = parseFloat(rSlider.value);
+ let d = parseFloat(dSlider.value);
+ let angle = 0;
+ let hue = 0;
+
+ function drawSpirograph() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.save();
+ ctx.translate(canvas.width / 2, canvas.height / 2);
+ ctx.scale(1.5, 1.5);
+
+ const centerX = 0;
+ const centerY = 0;
+ const points = [];
+
+ const totalPoints = 1000;
+ for (let i = 0; i < totalPoints; i++) {
+ const theta = (i / totalPoints) * 2 * Math.PI;
+ const x = (R - r) * Math.cos(theta) + d * Math.cos(((R - r) / r) * theta);
+ const y = (R - r) * Math.sin(theta) - d * Math.sin(((R - r) / r) * theta);
+ points.push({ x, y });
+ }
+
+ ctx.beginPath();
+ for (let i = 0; i < points.length; i++) {
+ const p = points[i];
+ if (i === 0) {
+ ctx.moveTo(p.x, p.y);
+ } else {
+ ctx.lineTo(p.x, p.y);
+ }
+ }
+ ctx.strokeStyle = `hsl(${(hue + i * 0.5) % 360}, 100%, 50%)`;
+ ctx.stroke();
+ ctx.restore();
+
+ hue += 0.5;
+ if (hue >= 360) hue = 0;
+ }
+
+ function animate() {
+ drawSpirograph();
+ requestAnimationFrame(animate);
+ }
+
+ animate();
+
+ RSlider.addEventListener('input', () => {
+ R = parseFloat(RSlider.value);
+ });
+ rSlider.addEventListener('input', () => {
+ r = parseFloat(rSlider.value);
+ });
+ dSlider.addEventListener('input', () => {
+ d = parseFloat(dSlider.value);
+ });
+
+ clearBtn.addEventListener('click', () => {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ });
+
+ saveBtn.addEventListener('click', () => {
+ const link = document.createElement('a');
+ link.download = 'spirograph.png';
+ link.href = canvas.toDataURL();
+ link.click();
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a7640ab6a2fa/qwen3-14b.png b/data/artifacts/a7640ab6a2fa/qwen3-14b.png
new file mode 100644
index 0000000..032e2eb
Binary files /dev/null and b/data/artifacts/a7640ab6a2fa/qwen3-14b.png differ
diff --git a/data/artifacts/f0251ce85742/claude-code.html b/data/artifacts/f0251ce85742/claude-code.html
new file mode 100644
index 0000000..1136907
--- /dev/null
+++ b/data/artifacts/f0251ce85742/claude-code.html
@@ -0,0 +1,329 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>PONG · 2P</title>
+<style>
+ html,body{
+ margin:0;height:100%;
+ background:#05060a;
+ color:#e8faff;
+ font-family:"Courier New",Courier,monospace;
+ overflow:hidden;
+ display:flex;flex-direction:column;
+ align-items:center;justify-content:center;
+ user-select:none;
+ -webkit-user-select:none;
+ }
+ #wrap{
+ display:flex;flex-direction:column;align-items:center;gap:14px;
+ padding:16px;
+ }
+ h1{
+ margin:0;font-size:22px;letter-spacing:10px;font-weight:bold;
+ color:#00e5ff;text-shadow:0 0 12px rgba(0,229,255,.6);
+ }
+ #stage{
+ position:relative;
+ box-shadow:0 0 40px rgba(0,229,255,.15),inset 0 0 0 2px #143b44;
+ border-radius:6px;
+ background:#0a0c12;
+ }
+ canvas{display:block;border-radius:6px;image-rendering:pixelated;}
+ #hint{
+ font-size:12px;letter-spacing:2px;color:#4a6b73;text-align:center;line-height:1.7;
+ }
+ #hint b{color:#7fe9ff;}
+ .tag{
+ display:inline-block;padding:1px 7px;border:1px solid #24525c;border-radius:3px;
+ color:#9fe8f5;margin:0 2px;
+ }
+</style>
+</head>
+<body>
+<div id="wrap">
+ <h1>P O N G</h1>
+ <div id="stage">
+ <canvas id="game" width="900" height="560"></canvas>
+ </div>
+ <div id="hint">
+ LEFT <span class="tag">W</span><span class="tag">S</span>
+ RIGHT <span class="tag">▲</span><span class="tag">▼</span>
+ <span class="tag">SPACE</span> serve / restart
+ First to <b>11</b> wins
+ </div>
+</div>
+
+<script>
+(() => {
+ "use strict";
+ const canvas = document.getElementById("game");
+ const ctx = canvas.getContext("2d");
+ const W = canvas.width, H = canvas.height;
+
+ // ---------- Audio (WebAudio retro beep) ----------
+ let actx = null;
+ function ensureAudio(){
+ if(!actx){
+ try { actx = new (window.AudioContext || window.webkitAudioContext)(); }
+ catch(e){ actx = null; }
+ }
+ if(actx && actx.state === "suspended") actx.resume();
+ }
+ function beep(freq, dur, type){
+ if(!actx) return;
+ const t = actx.currentTime;
+ const osc = actx.createOscillator();
+ const gain = actx.createGain();
+ osc.type = type || "square";
+ osc.frequency.setValueAtTime(freq, t);
+ gain.gain.setValueAtTime(0.0001, t);
+ gain.gain.exponentialRampToValueAtTime(0.18, t + 0.005);
+ gain.gain.exponentialRampToValueAtTime(0.0001, t + dur);
+ osc.connect(gain); gain.connect(actx.destination);
+ osc.start(t); osc.stop(t + dur);
+ }
+ const sndPaddle = () => beep(440, 0.06, "square");
+ const sndWall = () => beep(240, 0.05, "square");
+ const sndScore = () => beep(150, 0.22, "sawtooth");
+ const sndWin = () => { beep(523,0.12,"square"); setTimeout(()=>beep(659,0.12,"square"),120); setTimeout(()=>beep(880,0.22,"square"),240); };
+
+ // ---------- Game constants ----------
+ const WIN_SCORE = 11;
+ const PADDLE_W = 14, PADDLE_H = 96;
+ const PADDLE_MARGIN = 26;
+ const PADDLE_SPEED = 560; // px/sec
+ const BALL_SIZE = 14;
+ const BALL_START_SPEED = 360; // px/sec
+ const BALL_SPEED_INC = 26; // added each paddle hit
+ const BALL_MAX_SPEED = 920;
+ const MAX_BOUNCE_ANGLE = Math.PI * 0.38; // ~68deg
+
+ // ---------- State ----------
+ const STATE = { SERVE:0, PLAY:1, WIN:2 };
+ let state = STATE.SERVE;
+
+ const left = { x: PADDLE_MARGIN, y: H/2 - PADDLE_H/2, score: 0 };
+ const right = { x: W - PADDLE_MARGIN - PADDLE_W, y: H/2 - PADDLE_H/2, score: 0 };
+
+ const ball = { x: W/2, y: H/2, vx: 0, vy: 0, speed: BALL_START_SPEED };
+ let serveDir = Math.random() < 0.5 ? -1 : 1;
+ let winner = "";
+
+ const keys = Object.create(null);
+
+ function resetBall(dir){
+ ball.x = W/2; ball.y = H/2;
+ ball.speed = BALL_START_SPEED;
+ const angle = (Math.random() * 0.5 - 0.25) * Math.PI; // -45..45deg
+ ball.vx = Math.cos(angle) * ball.speed * dir;
+ ball.vy = Math.sin(angle) * ball.speed;
+ }
+
+ function serve(){
+ resetBall(serveDir);
+ state = STATE.PLAY;
+ }
+
+ function newGame(){
+ left.score = 0; right.score = 0;
+ left.y = right.y = H/2 - PADDLE_H/2;
+ winner = "";
+ serveDir = Math.random() < 0.5 ? -1 : 1;
+ state = STATE.SERVE;
+ }
+
+ // ---------- Input ----------
+ window.addEventListener("keydown", (e) => {
+ const k = e.key.length === 1 ? e.key.toLowerCase() : e.key;
+ keys[k] = true;
+ if(["ArrowUp","ArrowDown"," "].includes(e.key)) e.preventDefault();
+ if(e.key === " "){
+ ensureAudio();
+ if(state === STATE.SERVE) serve();
+ else if(state === STATE.WIN) newGame();
+ }
+ }, {passive:false});
+ window.addEventListener("keyup", (e) => {
+ const k = e.key.length === 1 ? e.key.toLowerCase() : e.key;
+ keys[k] = false;
+ });
+ canvas.addEventListener("mousedown", ensureAudio);
+
+ // ---------- Update ----------
+ function clamp(v, lo, hi){ return v < lo ? lo : v > hi ? hi : v; }
+
+ function movePaddles(dt){
+ if(keys["w"]) left.y -= PADDLE_SPEED * dt;
+ if(keys["s"]) left.y += PADDLE_SPEED * dt;
+ if(keys["ArrowUp"]) right.y -= PADDLE_SPEED * dt;
+ if(keys["ArrowDown"]) right.y += PADDLE_SPEED * dt;
+ left.y = clamp(left.y, 0, H - PADDLE_H);
+ right.y = clamp(right.y, 0, H - PADDLE_H);
+ }
+
+ function collidePaddle(pad, dir){
+ const bx = ball.x - BALL_SIZE/2, by = ball.y - BALL_SIZE/2;
+ if(bx < pad.x + PADDLE_W && bx + BALL_SIZE > pad.x &&
+ by < pad.y + PADDLE_H && by + BALL_SIZE > pad.y){
+ // Where on the paddle did it hit? -1 (top) .. 1 (bottom)
+ const rel = ((ball.y - (pad.y + PADDLE_H/2)) / (PADDLE_H/2));
+ const clamped = clamp(rel, -1, 1);
+ const angle = clamped * MAX_BOUNCE_ANGLE;
+ ball.speed = Math.min(ball.speed + BALL_SPEED_INC, BALL_MAX_SPEED);
+ ball.vx = dir * Math.cos(angle) * ball.speed;
+ ball.vy = Math.sin(angle) * ball.speed;
+ // nudge ball outside the paddle so it can't stick
+ if(dir > 0) ball.x = pad.x + PADDLE_W + BALL_SIZE/2 + 0.5;
+ else ball.x = pad.x - BALL_SIZE/2 - 0.5;
+ sndPaddle();
+ return true;
+ }
+ return false;
+ }
+
+ function updateBall(dt){
+ ball.x += ball.vx * dt;
+ ball.y += ball.vy * dt;
+
+ // top/bottom walls
+ if(ball.y - BALL_SIZE/2 <= 0){
+ ball.y = BALL_SIZE/2; ball.vy = Math.abs(ball.vy); sndWall();
+ } else if(ball.y + BALL_SIZE/2 >= H){
+ ball.y = H - BALL_SIZE/2; ball.vy = -Math.abs(ball.vy); sndWall();
+ }
+
+ // paddles
+ if(ball.vx < 0) collidePaddle(left, 1);
+ else collidePaddle(right, -1);
+
+ // scoring
+ if(ball.x + BALL_SIZE/2 < 0){
+ right.score++; sndScore(); afterPoint(1);
+ } else if(ball.x - BALL_SIZE/2 > W){
+ left.score++; sndScore(); afterPoint(-1);
+ }
+ }
+
+ function afterPoint(nextDir){
+ if(left.score >= WIN_SCORE || right.score >= WIN_SCORE){
+ winner = left.score > right.score ? "LEFT" : "RIGHT";
+ state = STATE.WIN;
+ sndWin();
+ } else {
+ serveDir = nextDir;
+ resetBall(serveDir);
+ state = STATE.SERVE;
+ }
+ }
+
+ // ---------- Render ----------
+ function drawNet(){
+ ctx.fillStyle = "#1b3a42";
+ const seg = 18, gap = 14, x = W/2 - 2;
+ for(let y = 8; y < H; y += seg + gap){
+ ctx.fillRect(x, y, 4, seg);
+ }
+ }
+
+ function drawScores(){
+ ctx.fillStyle = "#00e5ff";
+ ctx.font = "bold 72px 'Courier New',monospace";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "top";
+ ctx.globalAlpha = 0.85;
+ ctx.fillText(String(left.score), W*0.25, 24);
+ ctx.fillText(String(right.score), W*0.75, 24);
+ ctx.globalAlpha = 1;
+ }
+
+ function drawPaddle(p, color){
+ ctx.fillStyle = color;
+ ctx.shadowColor = color;
+ ctx.shadowBlur = 16;
+ ctx.fillRect(p.x, p.y, PADDLE_W, PADDLE_H);
+ ctx.shadowBlur = 0;
+ }
+
+ function drawBall(){
+ ctx.fillStyle = "#ffffff";
+ ctx.shadowColor = "#7fe9ff";
+ ctx.shadowBlur = 18;
+ ctx.fillRect(ball.x - BALL_SIZE/2, ball.y - BALL_SIZE/2, BALL_SIZE, BALL_SIZE);
+ ctx.shadowBlur = 0;
+ }
+
+ function centerText(lines){
+ ctx.textAlign = "center";
+ ctx.textBaseline = "middle";
+ let y = H/2;
+ ctx.fillStyle = "#00e5ff";
+ ctx.font = "bold 40px 'Courier New',monospace";
+ ctx.fillText(lines[0], W/2, y - 30);
+ ctx.fillStyle = "#9fe8f5";
+ ctx.font = "18px 'Courier New',monospace";
+ if(lines[1]) ctx.fillText(lines[1], W/2, y + 26);
+ if(lines[2]) ctx.fillText(lines[2], W/2, y + 56);
+ }
+
+ function render(){
+ ctx.clearRect(0,0,W,H);
+ ctx.fillStyle = "#0a0c12";
+ ctx.fillRect(0,0,W,H);
+
+ drawNet();
+ drawScores();
+ drawPaddle(left, "#3cff9e");
+ drawPaddle(right, "#ff5ca8");
+
+ if(state === STATE.PLAY){
+ drawBall();
+ } else if(state === STATE.SERVE){
+ drawBall();
+ centerText([
+ "READY",
+ "Press SPACE to serve",
+ (serveDir < 0 ? "◀ serving LEFT" : "serving RIGHT ▶")
+ ]);
+ } else if(state === STATE.WIN){
+ const color = winner === "LEFT" ? "#3cff9e" : "#ff5ca8";
+ ctx.textAlign = "center"; ctx.textBaseline = "middle";
+ ctx.fillStyle = color;
+ ctx.shadowColor = color; ctx.shadowBlur = 24;
+ ctx.font = "bold 52px 'Courier New',monospace";
+ ctx.fillText(winner + " PLAYER WINS!", W/2, H/2 - 30);
+ ctx.shadowBlur = 0;
+ ctx.fillStyle = "#9fe8f5";
+ ctx.font = "20px 'Courier New',monospace";
+ ctx.fillText(left.score + " — " + right.score, W/2, H/2 + 24);
+ ctx.fillStyle = "#7fe9ff";
+ ctx.font = "16px 'Courier New',monospace";
+ ctx.fillText("Press SPACE to play again", W/2, H/2 + 58);
+ }
+ }
+
+ // ---------- Loop ----------
+ let last = performance.now();
+ function loop(now){
+ let dt = (now - last) / 1000;
+ last = now;
+ if(dt > 0.05) dt = 0.05; // clamp big frame gaps
+
+ if(state === STATE.PLAY){
+ movePaddles(dt);
+ updateBall(dt);
+ } else {
+ movePaddles(dt); // allow positioning before serve
+ }
+
+ render();
+ requestAnimationFrame(loop);
+ }
+
+ newGame();
+ requestAnimationFrame(loop);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f0251ce85742/claude-code.png b/data/artifacts/f0251ce85742/claude-code.png
new file mode 100644
index 0000000..166b9e3
Binary files /dev/null and b/data/artifacts/f0251ce85742/claude-code.png differ
diff --git a/data/artifacts/f0251ce85742/gemma3-12b.html b/data/artifacts/f0251ce85742/gemma3-12b.html
new file mode 100644
index 0000000..f6254a7
--- /dev/null
+++ b/data/artifacts/f0251ce85742/gemma3-12b.html
@@ -0,0 +1,204 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Pong - AI Arena Challenge</title>
+<style>
+body { margin: 0; overflow: hidden; background-color: black; }
+canvas { display: block; }
+</style>
+</head>
+<body>
+
+<canvas id="pongCanvas" width="600" height="400"></canvas>
+
+<script>
+const canvas = document.getElementById('pongCanvas');
+const ctx = canvas.getContext('2d');
+
+let paddleLeftX = 20;
+let paddleLeftY = 150;
+let paddleLeftWidth = 10;
+let paddleLeftHeight = 80;
+let paddleLeftSpeed = 5;
+
+let paddleRightX = 570;
+let paddleRightY = 150;
+let paddleRightWidth = 10;
+let paddleRightHeight = 80;
+let paddleRightSpeed = 5;
+
+let ballX = 300;
+let ballY = 200;
+let ballSize = 10;
+let ballSpeedX = 3;
+let ballSpeedY = 3;
+let ballAngleMultiplier = 1.05; // Increase angle on paddle hit
+
+let scoreLeft = 0;
+let scoreRight = 0;
+let winningScore = 11;
+
+let audioContext;
+let beepSource;
+function playBeep() {
+ if(!audioContext) return;
+ beepSource.start(0); // Start immediately
+}
+
+
+function initAudio(){
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
+ beepSource = audioContext.createOscillator();
+ beepSource.type = 'sine';
+ beepSource.frequency.setValueAtTime(440, audioContext.currentTime); // A4 note
+ beepSource.connect(audioContext.destination);
+}
+
+
+function drawPaddle(x, y, width, height, color) {
+ ctx.fillStyle = color;
+ ctx.fillRect(x, y, width, height);
+}
+
+function drawBall(x, y, size) {
+ ctx.beginPath();
+ ctx.arc(x, y, size, 0, Math.PI * 2);
+ ctx.fillStyle = 'white';
+ ctx.fill();
+ ctx.closePath();
+}
+
+function drawScore() {
+ ctx.font = "30px Arial";
+ ctx.fillStyle = "white";
+ ctx.fillText("Left: " + scoreLeft, canvas.width / 2 - 80, 30);
+ ctx.fillText("Right: " + scoreRight, canvas.width / 2 + 20, 30);
+}
+
+function drawWinScreen(text) {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.font = "40px Arial";
+ ctx.fillStyle = "white";
+ ctx.textAlign = "center";
+ ctx.fillText(text, canvas.width / 2, canvas.height / 2);
+ ctx.font = "20px Arial";
+ ctx.fillText("Press Space to Restart", canvas.width / 2, canvas.height / 2 + 40);
+}
+
+function resetGame() {
+ paddleLeftY = 150;
+ paddleRightY = 150;
+ ballX = 300;
+ ballY = 200;
+ ballSpeedX = 3;
+ ballSpeedY = 3;
+ scoreLeft = 0;
+ scoreRight = 0;
+}
+
+function gameLoop() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Paddle Movement
+ if (keys['w'] && paddleLeftY > 0) {
+ paddleLeftY -= paddleLeftSpeed;
+ }
+ if (keys['s'] && paddleLeftY < canvas.height - paddleLeftHeight) {
+ paddleLeftY += paddleLeftSpeed;
+ }
+ if (keys['ArrowUp'] && paddleRightY > 0) {
+ paddleRightY -= paddleRightSpeed;
+ }
+ if (keys['ArrowDown'] && paddleRightY < canvas.height - paddleRightHeight) {
+ paddleRightY += paddleRightSpeed;
+ }
+
+ // Ball Movement
+ ballX += ballSpeedX;
+ ballY += ballSpeedY;
+
+ // Wall Collision
+ if (ballY + ballSize > canvas.height || ballY - ballSize < 0) {
+ ballSpeedY = -ballSpeedY;
+ }
+
+ // Paddle Collision
+ if (
+ ballX - ballSize < paddleLeftWidth + paddleLeftX &&
+ ballY > paddleLeftY &&
+ ballY < paddleLeftY + paddleLeftHeight
+ ) {
+ ballSpeedX = -ballSpeedX * ballAngleMultiplier;
+ playBeep();
+ }
+
+ if (
+ ballX + ballSize > canvas.width - paddleRightWidth - paddleRightX &&
+ ballY > paddleRightY &&
+ ballY < paddleRightY + paddleRightHeight
+ ) {
+ ballSpeedX = -ballSpeedX * ballAngleMultiplier;
+ playBeep();
+ }
+
+ // Scoring
+ if (ballX - ballSize < 0) {
+ scoreRight++;
+ resetGame();
+ }
+ if (ballX + ballSize > canvas.width) {
+ scoreLeft++;
+ resetGame();
+ }
+
+
+ // Drawing
+ drawPaddle(paddleLeftX, paddleLeftY, paddleLeftWidth, paddleLeftHeight, 'white');
+ drawPaddle(paddleRightX, paddleRightY, paddleRightWidth, paddleRightHeight, 'white');
+ drawBall(ballX, ballY, ballSize);
+ drawScore();
+
+ // Win Condition
+ if (scoreLeft >= winningScore) {
+ drawWinScreen("Left Player Wins!");
+ return; // Stop game loop
+ }
+ if (scoreRight >= winningScore) {
+ drawWinScreen("Right Player Wins!");
+ return; // Stop game loop
+ }
+
+ requestAnimationFrame(gameLoop);
+}
+
+
+
+let keys = {};
+document.addEventListener('keydown', function(e) {
+ keys[e.key] = true;
+});
+
+document.addEventListener('keyup', function(e) {
+ keys[e.key] = false;
+});
+
+
+initAudio();
+
+canvas.addEventListener("click", function(){
+ if (audioContext && audioContext.state === 'suspended'){
+ audioContext.resume();
+ }
+});
+
+
+
+// Start Game
+resetGame();
+gameLoop();
+
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f0251ce85742/gemma3-12b.png b/data/artifacts/f0251ce85742/gemma3-12b.png
new file mode 100644
index 0000000..ad57eb4
Binary files /dev/null and b/data/artifacts/f0251ce85742/gemma3-12b.png differ
diff --git a/data/artifacts/f0251ce85742/hermes3-8b.html b/data/artifacts/f0251ce85742/hermes3-8b.html
new file mode 100644
index 0000000..35654cd
--- /dev/null
+++ b/data/artifacts/f0251ce85742/hermes3-8b.html
@@ -0,0 +1,185 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Two-Player Pong</title>
+ <style>
+ canvas {
+ border: 1px solid black;
+ }
+ </style>
+</head>
+<body>
+ <script>
+ const canvas = document.createElement('canvas');
+ canvas.width = 800;
+ canvas.height = 400;
+ document.body.appendChild(canvas);
+
+ const ctx = canvas.getContext('2d');
+
+ let paddleWidth = 10;
+ let paddleHeight = 60;
+ let ballRadius = 5;
+
+ let leftPaddle = {
+ x: 0,
+ y: (canvas.height - paddleHeight) / 2
+ };
+
+ let rightPaddle = {
+ x: canvas.width - paddleWidth,
+ y: (canvas.height - paddleHeight) / 2
+ };
+
+ let ball = {
+ x: canvas.width / 2,
+ y: canvas.height / 2,
+ speedX: 3,
+ speedY: 2,
+ angleLeftPaddle: 0,
+ angleRightPaddle: 0
+ };
+
+ function resetBall() {
+ ball.x = canvas.width / 2;
+ ball.y = canvas.height / 2;
+ ball.speedX *= -1;
+ ball.speedY = Math.random() * 5 - 2.5;
+ }
+
+ function updatePaddles() {
+ if (event.key === 'w' && leftPaddle.y > 0) leftPaddle.y -= 10;
+ if (event.key === 's' && leftPaddle.y < canvas.height - paddleHeight) leftPaddle.y += 10;
+
+ if (event.keyCode === 37 && rightPaddle.y > 0) rightPaddle.y -= 10;
+ if (event.keyCode === 39 && rightPaddle.y < canvas.height - paddleHeight) rightPaddle.y += 10;
+ }
+
+ function updateBall() {
+ ball.x += ball.speedX;
+ ball.y += ball.speedY;
+
+ if (ball.y + ballRadius > canvas.height || ball.y - ballRadius < 0) {
+ ball.speedY *= -1;
+ }
+
+ if (
+ ball.x - ballRadius < leftPaddle.x &&
+ ball.y > leftPaddle.y &&
+ ball.y < leftPaddle.y + paddleHeight
+ ) {
+ ball.speedX = Math.abs(ball.speedX) * 1.05;
+ ball.angleLeftPaddle++;
+ playHitSound();
+ } else if (
+ ball.x + ballRadius > rightPaddle.x &&
+ ball.y > rightPaddle.y &&
+ ball.y < rightPaddle.y + paddleHeight
+ ) {
+ ball.speedX = Math.abs(ball.speedX) * 1.05;
+ ball.angleRightPaddle++;
+ playHitSound();
+ }
+
+ if (ball.x - ballRadius < 0 || ball.x + ballRadius > canvas.width) {
+ resetBall();
+ updateScores();
+ }
+ }
+
+ function playHitSound() {
+ const hitAudio = new Audio('hit.wav');
+ hitAudio.play();
+ }
+
+ function updateScores() {
+ let leftScore = parseInt(localStorage.getItem('leftScore')) || 0;
+ let rightScore = parseInt(localStorage.getItem('rightScore')) || 0;
+
+ if (ball.x - ballRadius < 0) {
+ leftScore++;
+ localStorage.setItem('leftScore', leftScore);
+ localStorage.setItem('rightScore', rightScore);
+ } else {
+ rightScore++;
+ localStorage.setItem('leftScore', leftScore);
+ localStorage.setItem('rightScore', rightScore);
+ }
+
+ if (leftScore === 11 || rightScore === 11) {
+ showWinScreen(leftScore, rightScore);
+ }
+ }
+
+ function drawPaddles() {
+ ctx.fillStyle = 'black';
+ ctx.fillRect(leftPaddle.x, leftPaddle.y, paddleWidth, paddleHeight);
+ ctx.fillRect(rightPaddle.x, rightPaddle.y, paddleWidth, paddleHeight);
+ }
+
+ function drawBall() {
+ ctx.beginPath();
+ ctx.arc(ball.x, ball.y, ballRadius, 0, Math.PI * 2);
+ ctx.fillStyle = 'black';
+ ctx.fill();
+ }
+
+ function drawScores() {
+ ctx.font = '24px Arial';
+ ctx.fillStyle = 'black';
+
+ const leftScoreText = localStorage.getItem('leftScore');
+ const rightScoreText = localStorage.getItem('rightScore');
+
+ ctx.fillText(leftScoreText, canvas.width / 4, 30);
+ ctx.fillText(rightScoreText, (canvas.width * 3) / 4, 30);
+ }
+
+ function drawWinScreen() {
+ ctx.font = '48px Arial';
+ ctx.fillStyle = 'black';
+
+ const leftScoreText = localStorage.getItem('leftScore');
+ const rightScoreText = localStorage.getItem('rightScore');
+
+ if (leftScore === 11) {
+ ctx.fillText(leftScoreText + ' wins!', canvas.width / 2 - 120, canvas.height / 2);
+ } else {
+ ctx.fillText(rightScoreText + ' wins!', canvas.width / 2 - 120, canvas.height / 2);
+ }
+
+ const restartText = 'Press any key to restart';
+ ctx.font = '24px Arial';
+ ctx.fillStyle = 'black';
+ ctx.fillText(restartText, canvas.width / 2 - 100, canvas.height * 3 / 4);
+ }
+
+ function gameLoop() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ updatePaddles();
+ updateBall();
+
+ drawPaddles();
+ drawBall();
+ drawScores();
+
+ if (showWinScreen) return;
+
+ requestAnimationFrame(gameLoop);
+ }
+
+ document.addEventListener('keydown', updatePaddles);
+
+ let showWinScreen = false;
+ let leftScore = parseInt(localStorage.getItem('leftScore')) || 0;
+ let rightScore = parseInt(localStorage.getItem('rightScore')) || 0;
+
+ if (leftScore === 11 || rightScore === 11) {
+ showWinScreen = true;
+ }
+
+ gameLoop();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f0251ce85742/hermes3-8b.png b/data/artifacts/f0251ce85742/hermes3-8b.png
new file mode 100644
index 0000000..ee16dd8
Binary files /dev/null and b/data/artifacts/f0251ce85742/hermes3-8b.png differ
diff --git a/data/artifacts/f0251ce85742/hf-qwen-coder-32b.html b/data/artifacts/f0251ce85742/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..c806e05
--- /dev/null
+++ b/data/artifacts/f0251ce85742/hf-qwen-coder-32b.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Pong</title>
+<style>
+ body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #000; }
+ canvas { border: 2px solid #fff; }
+</style>
+</head>
+<body>
+<canvas id="pongCanvas" width="640" height="480"></canvas>
+<script>
+const canvas = document.getElementById('pongCanvas');
+const ctx = canvas.getContext('2d');
+const paddleWidth = 15, paddleHeight = 90;
+let leftPaddleY = (canvas.height - paddleHeight) / 2;
+let rightPaddleY = (canvas.height - paddleHeight) / 2;
+let ballX = canvas.width / 2, ballY = canvas.height / 2;
+let ballRadius = 10;
+let ballSpeedX = 5, ballSpeedY = 5;
+let scoreLeft = 0, scoreRight = 0;
+const beepSound = (function() {
+ const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ return function() {
+ const oscillator = audioCtx.createOscillator();
+ oscillator.type = 'sine';
+ oscillator.frequency.setValueAtTime(440, audioCtx.currentTime);
+ oscillator.connect(audioCtx.destination);
+ oscillator.start();
+ oscillator.stop(audioCtx.currentTime + 0.1);
+ };
+})();
+function drawPaddle(x, y) {
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(x, y, paddleWidth, paddleHeight);
+}
+function drawBall() {
+ ctx.beginPath();
+ ctx.arc(ballX, ballY, ballRadius, 0, Math.PI * 2);
+ ctx.fillStyle = '#fff';
+ ctx.fill();
+ ctx.closePath();
+}
+function drawScore() {
+ ctx.font = '48px Arial';
+ ctx.fillText(scoreLeft, canvas.width / 4, 50);
+ ctx.fillText(scoreRight, 3 * canvas.width / 4, 50);
+}
+function moveBall() {
+ ballX += ballSpeedX;
+ ballY += ballSpeedY;
+
+ if (ballY + ballRadius > canvas.height || ballY - ballRadius < 0) {
+ ballSpeedY = -ballSpeedY;
+ }
+ const paddleTolerance = 10;
+ if ((ballX - ballRadius <= paddleWidth && ballY >= leftPaddleY - paddleTolerance && ballY <= leftPaddleY + paddleHeight + paddleTolerance) ||
+ (ballX + ballRadius >= canvas.width - paddleWidth && ballY >= rightPaddleY - paddleTolerance && ballY <= rightPaddleY + paddleHeight + paddleTolerance)) {
+ beepSound();
+ ballSpeedX = -ballSpeedX;
+ const relativeIntersectY = (ballY - ((leftPaddleY + rightPaddleY) / 2));
+ const normalizedRelativeIntersectionY = relativeIntersectY / (paddleHeight / 2);
+ const bounceAngle = normalizedRelativeIntersectionY * Math.PI / 4;
+ ballSpeedX *= 1.05;
+ ballSpeedY = ballSpeedX * Math.tan(bounceAngle);
+ }
+}
+function resetBall() {
+ ballX = canvas.width / 2;
+ ballY = canvas.height / 2;
+ ballSpeedX = -ballSpeedX;
+ ballSpeedY = 3 + Math.random() * 4 - 2;
+}
+function checkScore() {
+ if (ballX < 0) {
+ scoreRight++;
+ resetBall();
+ } else if (ballX > canvas.width) {
+ scoreLeft++;
+ resetBall();
+ }
+ if (scoreLeft >= 11 || scoreRight >= 11) {
+ alert((scoreLeft > scoreRight ? 'Left' : 'Right') + ' player wins!');
+ scoreLeft = scoreRight = 0;
+ }
+}
+function gameLoop() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ drawPaddle(0, leftPaddleY);
+ drawPaddle(canvas.width - paddleWidth, rightPaddleY);
+ moveBall();
+ checkScore();
+ drawBall();
+ drawScore();
+ requestAnimationFrame(gameLoop);
+}
+document.addEventListener('keydown', function(e) {
+ if (e.key === 'w' && leftPaddleY > 0) leftPaddleY -= 15;
+ else if (e.key === 's' && leftPaddleY < canvas.height - paddleHeight) leftPaddleY += 15;
+ else if (e.key === 'ArrowUp' && rightPaddleY > 0) rightPaddleY -= 15;
+ else if (e.key === 'ArrowDown' && rightPaddleY < canvas.height - paddleHeight) rightPaddleY += 15;
+});
+gameLoop();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f0251ce85742/hf-qwen-coder-32b.png b/data/artifacts/f0251ce85742/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..41c1eb7
Binary files /dev/null and b/data/artifacts/f0251ce85742/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/f0251ce85742/qwen25-7b.html b/data/artifacts/f0251ce85742/qwen25-7b.html
new file mode 100644
index 0000000..c6fa59a
--- /dev/null
+++ b/data/artifacts/f0251ce85742/qwen25-7b.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Two-Player Pong</title>
+ <style>
+ body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #000; }
+ canvas { border: 5px solid #fff; box-shadow: 0 0 20px rgba(255,255,255,.7); }
+ </style>
+</head>
+<body>
+<canvas id="pong" width="640" height="320"></canvas>
+<script>
+const canvas = document.getElementById('pong');
+const ctx = canvas.getContext('2d');
+
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight / 2;
+
+const paddleWidth = 10, paddleHeight = 80;
+let leftPaddleY = (canvas.height - paddleHeight) / 2;
+let rightPaddleY = (canvas.height - paddleHeight) / 2;
+let ballX = canvas.width / 2, ballY = canvas.height / 2, ballSpeed = 4, ballDirX = 1, ballDirY = 1;
+
+const leftScore = 0, rightScore = 0;
+
+function drawPaddle(x, y) {
+ ctx.fillStyle = '#fff';
+ ctx.fillRect(x, y, paddleWidth, paddleHeight);
+}
+
+function drawBall() {
+ ctx.fillStyle = '#fff';
+ ctx.beginPath();
+ ctx.arc(ballX, ballY, 10, 0, Math.PI * 2);
+ ctx.fill();
+}
+
+function movePaddle(y, player) {
+ if (player === 'left' && y > 0 && y < canvas.height - paddleHeight)
+ leftPaddleY = y;
+ else if (player === 'right' && y > 0 && y < canvas.height - paddleHeight)
+ rightPaddleY = y;
+}
+
+function updateBall() {
+ ballX += ballDirX * ballSpeed;
+ ballY += ballDirY * ballSpeed;
+
+ // Ball collision with top/bottom
+ if (ballY <= 10 || ballY >= canvas.height - 10)
+ ballDirY *= -1;
+
+ // Ball collision with paddles
+ if (
+ ballX < paddleWidth + 10 &&
+ ballY > leftPaddleY && ballY < leftPaddleY + paddleHeight &&
+ ballDirX === -1
+ ) {
+ ballDirX = 1;
+ ballDirY = (ballY - (leftPaddleY + paddleHeight / 2)) * .3 + ballDirY;
+ ballSpeed += .25;
+ playSound();
+ }
+ if (
+ ballX > canvas.width - 10 - paddleWidth &&
+ ballY > rightPaddleY && ballY < rightPaddleY + paddleHeight &&
+ ballDirX === 1
+ ) {
+ ballDirX = -1;
+ ballDirY = (ballY - (rightPaddleY + paddleHeight / 2)) * .3 + ballDirY;
+ ballSpeed += .25;
+ playSound();
+ }
+
+ // Ball scoring
+ if (ballX <= 0) {
+ rightScore++;
+ resetBall();
+ checkWinner();
+ }
+ if (ballX >= canvas.width - paddleWidth) {
+ leftScore++;
+ resetBall();
+ checkWinner();
+ }
+}
+
+function resetBall() {
+ ballX = canvas.width / 2;
+ ballY = canvas.height / 2;
+ ballSpeed = 4;
+ ballDirX = Math.random() < 0.5 ? -1 : 1;
+ ballDirY = (Math.random() * 2 - 1) + 0.5;
+}
+
+function checkWinner() {
+ if (leftScore >= 11 || rightScore >= 11)
+ showWinScreen();
+}
+
+function playSound() {
+ const audio = new Audio('data:audio/wave;base64,dANjvbQAAAAAAAC8gAABzvYbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
\ No newline at end of file
diff --git a/data/artifacts/f0251ce85742/qwen25-7b.png b/data/artifacts/f0251ce85742/qwen25-7b.png
new file mode 100644
index 0000000..e24ceaa
Binary files /dev/null and b/data/artifacts/f0251ce85742/qwen25-7b.png differ
diff --git a/data/artifacts/f0251ce85742/qwen3-14b.html b/data/artifacts/f0251ce85742/qwen3-14b.html
new file mode 100644
index 0000000..144ec4d
--- /dev/null
+++ b/data/artifacts/f0251ce85742/qwen3-14b.html
@@ -0,0 +1,243 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Two-Player Pong</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: black;
+ color: white;
+ font-family: monospace;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ }
+ canvas {
+ background: #000;
+ display: block;
+ }
+ #gameOver {
+ position: absolute;
+ top: 40%;
+ text-align: center;
+ font-size: 3em;
+ width: 100%;
+ }
+ #restart {
+ position: absolute;
+ top: 50%;
+ text-align: center;
+ font-size: 1.5em;
+ width: 100%;
+ cursor: pointer;
+ }
+ </style>
+</head>
+<body>
+<canvas id="pongCanvas" width="800" height="400"></canvas>
+<div id="gameOver" style="display:none;">Game Over</div>
+<div id="restart" style="display:none;" onclick="init()">Press to Restart</div>
+<script>
+const canvas = document.getElementById('pongCanvas');
+const ctx = canvas.getContext('2d');
+
+const width = canvas.width;
+const height = canvas.height;
+
+let leftPaddle = { x: 10, y: 150, width: 10, height: 100, dy: 0 };
+let rightPaddle = { x: width - 20, y: 150, width: 10, height: 100, dy: 0 };
+let ball = {
+ x: width / 2,
+ y: height / 2,
+ radius: 10,
+ dx: 4,
+ dy: 4,
+ speed: 4
+};
+let leftScore = 0;
+let rightScore = 0;
+let gameOver = false;
+
+const paddleSpeed = 6;
+const winScore = 11;
+
+function drawPaddle(paddle) {
+ ctx.fillStyle = 'white';
+ ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
+}
+
+function drawBall() {
+ ctx.beginPath();
+ ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
+ ctx.fillStyle = 'white';
+ ctx.fill();
+ ctx.closePath();
+}
+
+function drawNet() {
+ ctx.strokeStyle = 'white';
+ ctx.lineWidth = 2;
+ for (let i = 0; i < height; i += 20) {
+ ctx.beginPath();
+ ctx.moveTo(width / 2, i);
+ ctx.lineTo(width / 2, i + 10);
+ ctx.stroke();
+ }
+}
+
+function drawScore() {
+ ctx.font = '32px monospace';
+ ctx.fillText(leftScore, width / 4, 50);
+ ctx.fillText(rightScore, 3 * width / 4, 50);
+}
+
+function movePaddles() {
+ leftPaddle.y += leftPaddle.dy;
+ rightPaddle.y += rightPaddle.dy;
+
+ // Prevent paddles from moving out of bounds
+ if (leftPaddle.y < 0) leftPaddle.y = 0;
+ if (leftPaddle.y + leftPaddle.height > height) leftPaddle.y = height - leftPaddle.height;
+ if (rightPaddle.y < 0) rightPaddle.y = 0;
+ if (rightPaddle.y + rightPaddle.height > height) rightPaddle.y = height - rightPaddle.height;
+}
+
+function moveBall() {
+ ball.x += ball.dx;
+ ball.y += ball.dy;
+
+ // Bounce off top and bottom
+ if (ball.y - ball.radius < 0 || ball.y + ball.radius > height) {
+ ball.dy *= -1;
+ playSound('hit');
+ }
+
+ // Paddle collision
+ if (
+ ball.x - ball.radius < leftPaddle.x + leftPaddle.width &&
+ ball.y > leftPaddle.y &&
+ ball.y < leftPaddle.y + leftPaddle.height
+ ) {
+ ball.dx *= -1;
+ let relativeY = (ball.y - (leftPaddle.y + leftPaddle.height / 2)) / (leftPaddle.height / 2);
+ ball.dy = relativeY * 6;
+ ball.speed += 0.1;
+ playSound('hit');
+ }
+
+ if (
+ ball.x + ball.radius > rightPaddle.x &&
+ ball.y > rightPaddle.y &&
+ ball.y < rightPaddle.y + rightPaddle.height
+ ) {
+ ball.dx *= -1;
+ let relativeY = (ball.y - (rightPaddle.y + rightPaddle.height / 2)) / (rightPaddle.height / 2);
+ ball.dy = relativeY * 6;
+ ball.speed += 0.1;
+ playSound('hit');
+ }
+
+ // Score
+ if (ball.x - ball.radius < 0) {
+ rightScore++;
+ resetBall();
+ playSound('score');
+ }
+
+ if (ball.x + ball.radius > width) {
+ leftScore++;
+ resetBall();
+ playSound('score');
+ }
+}
+
+function resetBall() {
+ ball.x = width / 2;
+ ball.y = height / 2;
+ ball.dx = (Math.random() > 0.5 ? -1 : 1) * ball.speed;
+ ball.dy = (Math.random() - 0.5) * 8;
+}
+
+function draw() {
+ ctx.clearRect(0, 0, width, height);
+ drawNet();
+ drawPaddle(leftPaddle);
+ drawPaddle(rightPaddle);
+ drawBall();
+ drawScore();
+}
+
+function update() {
+ movePaddles();
+ moveBall();
+ draw();
+}
+
+function gameLoop() {
+ if (gameOver) return;
+ update();
+ requestAnimationFrame(gameLoop);
+}
+
+function checkWin() {
+ if (leftScore >= winScore || rightScore >= winScore) {
+ gameOver = true;
+ document.getElementById('gameOver').style.display = 'block';
+ document.getElementById('restart').style.display = 'block';
+ }
+}
+
+function init() {
+ leftScore = 0;
+ rightScore = 0;
+ gameOver = false;
+ resetBall();
+ document.getElementById('gameOver').style.display = 'none';
+ document.getElementById('restart').style.display = 'none';
+ gameLoop();
+}
+
+document.addEventListener('keydown', function(e) {
+ if (e.key === 'w') leftPaddle.dy = -paddleSpeed;
+ if (e.key === 's') leftPaddle.dy = paddleSpeed;
+ if (e.key === 'ArrowUp') rightPaddle.dy = -paddleSpeed;
+ if (e.key === 'ArrowDown') rightPaddle.dy = paddleSpeed;
+});
+
+document.addEventListener('keyup', function(e) {
+ if (e.key === 'w' || e.key === 's') leftPaddle.dy = 0;
+ if (e.key === 'ArrowUp' || e.key === 'ArrowDown') rightPaddle.dy = 0;
+});
+
+// Web Audio for sounds
+const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+
+function playSound(type) {
+ if (!audioCtx) return;
+ if (audioCtx.state === 'suspended') {
+ audioCtx.resume();
+ }
+
+ const osc = audioCtx.createOscillator();
+ const gain = audioCtx.createGain();
+
+ osc.type = 'square';
+ osc.frequency.value = type === 'hit' ? 1000 : 500;
+ gain.gain.setValueAtTime(0.1, audioCtx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.2);
+
+ osc.connect(gain);
+ gain.connect(audioCtx.destination);
+
+ osc.start();
+ osc.stop(audioCtx.currentTime + 0.2);
+}
+
+init();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f0251ce85742/qwen3-14b.png b/data/artifacts/f0251ce85742/qwen3-14b.png
new file mode 100644
index 0000000..937ef42
Binary files /dev/null and b/data/artifacts/f0251ce85742/qwen3-14b.png differ
diff --git a/data/challenges.json b/data/challenges.json
index d09db99..ad1501d 100644
--- a/data/challenges.json
+++ b/data/challenges.json
@@ -6165,7 +6165,8 @@
"started_at": "2026-07-23T17:18:20.817Z",
"finished_at": "2026-07-23T17:18:52.059Z",
"queued_at": "2026-07-23T17:17:55.844Z",
- "bytes": 3724
+ "bytes": 3724,
+ "thumb": true
},
{
"model": "hermes3-8b",
@@ -9829,7 +9830,7 @@
"category": "Games",
"designTools": false,
"created_at": "2026-07-23T18:49:00.915Z",
- "winner": null,
+ "winner": "claude-code",
"runs": [
{
"model": "qwen3-14b",
@@ -9841,7 +9842,14 @@
"finished_at": "2026-07-23T18:49:35.697Z",
"queued_at": "2026-07-23T18:49:00.920Z",
"bytes": 5085,
- "thumb": true
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The model fulfills the challenge by creating an interactive rope physics toy but lacks some visual polish and detail.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
},
{
"model": "gemma3-12b",
@@ -9853,17 +9861,25 @@
"finished_at": "2026-07-23T18:50:31.909Z",
"queued_at": "2026-07-23T18:49:00.925Z",
"bytes": 4201,
- "thumb": true
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is interactive and fulfills the requirements but lacks visual appeal.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
},
{
"model": "hermes3-8b",
- "status": "running",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
+ "seconds": 37,
+ "cost": 0,
"started_at": "2026-07-23T18:50:31.916Z",
- "finished_at": null,
- "queued_at": "2026-07-23T18:49:00.929Z"
+ "finished_at": "2026-07-23T18:51:08.852Z",
+ "queued_at": "2026-07-23T18:49:00.929Z",
+ "bytes": 4879
},
{
"model": "qwen25-7b",
@@ -9875,17 +9891,26 @@
"finished_at": "2026-07-23T18:49:18.251Z",
"queued_at": "2026-07-23T18:49:00.933Z",
"bytes": 2448,
- "thumb": true
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The model fulfills the challenge but lacks interactivity and visual appeal.",
+ "aiScores": {
+ "qwen2.5vl:7b": 6,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
},
{
"model": "hf-qwen-coder-32b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-23T18:49:00.936Z"
+ "seconds": 91,
+ "cost": 0,
+ "started_at": "2026-07-23T18:51:08.857Z",
+ "finished_at": "2026-07-23T18:52:40.174Z",
+ "queued_at": "2026-07-23T18:49:00.936Z",
+ "bytes": 3784,
+ "thumb": true
},
{
"model": "claude-code",
@@ -9897,7 +9922,937 @@
"finished_at": "2026-07-23T18:50:10.357Z",
"queued_at": "2026-07-23T18:49:00.940Z",
"bytes": 13057,
- "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
+ }
+ ],
+ "judged_at": "2026-07-23T18:53:14.555Z",
+ "judging": false,
+ "aiPick": "claude-code",
+ "voted_at": "2026-07-23T18:53:21.925Z"
+ },
+ {
+ "id": "7993245ce013",
+ "title": "Free-Roster — Emoji Memory Game",
+ "prompt": "Build a single-file HTML page: a memory match game. A grid of face-down cards with emoji pairs; flip two, match to keep them up; move counter and timer; win screen + restart; a difficulty selector (4x4 / 6x6). Smooth flip animation, self-contained, no external libraries.",
+ "category": "Games",
+ "designTools": false,
+ "created_at": "2026-07-23T18:53:37.559Z",
+ "winner": "claude-code",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 34,
+ "cost": 0,
+ "started_at": "2026-07-23T18:53:37.590Z",
+ "finished_at": "2026-07-23T18:54:11.602Z",
+ "queued_at": "2026-07-23T18:53:37.564Z",
+ "bytes": 5895,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The game is functional but lacks visual appeal and smooth animation.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 76,
+ "cost": 0,
+ "started_at": "2026-07-23T18:54:11.609Z",
+ "finished_at": "2026-07-23T18:55:27.588Z",
+ "queued_at": "2026-07-23T18:53:37.570Z",
+ "bytes": 6240,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The game is functional but lacks visual appeal and smooth animation.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 21,
+ "cost": 0,
+ "started_at": "2026-07-23T18:55:27.595Z",
+ "finished_at": "2026-07-23T18:55:48.356Z",
+ "queued_at": "2026-07-23T18:53:37.576Z",
+ "bytes": 6729,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The game is functional but lacks visual appeal and smooth animation.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 24,
+ "cost": 0,
+ "started_at": "2026-07-23T18:53:37.595Z",
+ "finished_at": "2026-07-23T18:54:01.872Z",
+ "queued_at": "2026-07-23T18:53:37.579Z",
+ "bytes": 4156,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The game is functional and meets the requirements but lacks visual appeal.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 53,
+ "cost": 0,
+ "started_at": "2026-07-23T18:55:48.359Z",
+ "finished_at": "2026-07-23T18:56:41.525Z",
+ "queued_at": "2026-07-23T18:53:37.583Z",
+ "bytes": 4298,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The game is functional but lacks visual appeal and smooth animation.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 43,
+ "cost": 0,
+ "started_at": "2026-07-23T18:53:37.601Z",
+ "finished_at": "2026-07-23T18:54:20.114Z",
+ "queued_at": "2026-07-23T18:53:37.587Z",
+ "bytes": 8521,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The game is fully functional and visually appealing, fulfilling all the requirements of the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T18:57:18.709Z",
+ "aiPick": "claude-code",
+ "voted_at": "2026-07-23T18:57:28.251Z"
+ },
+ {
+ "id": "a7640ab6a2fa",
+ "title": "Free-Roster — Spirograph",
+ "prompt": "Build a single-file HTML page: an animated spirograph drawing tool. Sliders for the two gear radii and pen offset draw hypotrochoid curves in real time with color cycling; clear and save-PNG buttons. Canvas-based, self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T18:57:42.693Z",
+ "winner": "claude-code",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 31,
+ "cost": 0,
+ "started_at": "2026-07-23T18:57:42.718Z",
+ "finished_at": "2026-07-23T18:58:13.820Z",
+ "queued_at": "2026-07-23T18:57:42.697Z",
+ "bytes": 3996,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The tool is functional and meets the requirements but lacks visual appeal.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 29,
+ "cost": 0,
+ "started_at": "2026-07-23T18:58:13.823Z",
+ "finished_at": "2026-07-23T18:58:43.148Z",
+ "queued_at": "2026-07-23T18:57:42.701Z",
+ "bytes": 2717,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The tool is functional and meets the challenge criteria but lacks polish in terms of visual quality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 19,
+ "cost": 0,
+ "started_at": "2026-07-23T18:58:43.152Z",
+ "finished_at": "2026-07-23T18:59:02.046Z",
+ "queued_at": "2026-07-23T18:57:42.704Z",
+ "bytes": 2363,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The tool is functional but lacks the clear and save-PNG buttons as specified.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 20,
+ "cost": 0,
+ "started_at": "2026-07-23T18:57:42.722Z",
+ "finished_at": "2026-07-23T18:58:03.109Z",
+ "queued_at": "2026-07-23T18:57:42.707Z",
+ "bytes": 2729,
+ "thumb": true,
+ "aiScore": 7,
+ "aiReason": "The tool is functional but lacks visual appeal and does not include color cycling.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 45,
+ "cost": 0,
+ "started_at": "2026-07-23T18:59:02.051Z",
+ "finished_at": "2026-07-23T18:59:47.117Z",
+ "queued_at": "2026-07-23T18:57:42.712Z",
+ "bytes": 3019,
+ "thumb": true,
+ "aiScore": 5.5,
+ "aiReason": "The tool is functional but lacks the clear and save-PNG buttons as specified.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 4
+ },
+ "aiSpread": 3
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 64,
+ "cost": 0,
+ "started_at": "2026-07-23T18:57:42.725Z",
+ "finished_at": "2026-07-23T18:58:46.920Z",
+ "queued_at": "2026-07-23T18:57:42.715Z",
+ "bytes": 11601,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The tool is fully functional and visually appealing, meeting all the challenge requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T19:01:02.560Z",
+ "aiPick": "claude-code",
+ "voted_at": "2026-07-23T19:01:03.331Z"
+ },
+ {
+ "id": "f0251ce85742",
+ "title": "Free-Roster — Pong 2P",
+ "prompt": "Build a single-file HTML page: two-player Pong. Left paddle W/S, right paddle arrow keys, ball with increasing speed and angle-on-paddle physics, score to 11, win screen + restart, retro beep on hit (WebAudio). Self-contained, no external assets.",
+ "category": "Games",
+ "designTools": false,
+ "created_at": "2026-07-23T19:01:19.269Z",
+ "winner": "qwen3-14b",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 42,
+ "cost": 0,
+ "started_at": "2026-07-23T19:01:19.281Z",
+ "finished_at": "2026-07-23T19:02:00.860Z",
+ "queued_at": "2026-07-23T19:01:19.271Z",
+ "bytes": 5687,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The game is functional and meets the challenge criteria but lacks some polish in terms of visual quality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 43,
+ "cost": 0,
+ "started_at": "2026-07-23T19:02:00.867Z",
+ "finished_at": "2026-07-23T19:02:43.705Z",
+ "queued_at": "2026-07-23T19:01:19.273Z",
+ "bytes": 4758,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The game is functional but lacks some visual polish and could benefit from more detailed graphics.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 32,
+ "cost": 0,
+ "started_at": "2026-07-23T19:02:43.722Z",
+ "finished_at": "2026-07-23T19:03:15.525Z",
+ "queued_at": "2026-07-23T19:01:19.274Z",
+ "bytes": 4936,
+ "thumb": true,
+ "aiScore": 5.8,
+ "aiReason": "The page is empty and does not fulfill the challenge requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 5,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 1.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 23,
+ "cost": 0,
+ "started_at": "2026-07-23T19:01:19.283Z",
+ "finished_at": "2026-07-23T19:01:42.122Z",
+ "queued_at": "2026-07-23T19:01:19.276Z",
+ "bytes": 3096,
+ "thumb": true,
+ "aiScore": 3.5,
+ "aiReason": "The image is blank and does not show any gameplay or visual elements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 3,
+ "minicpm-v:latest": 4
+ },
+ "aiSpread": 1
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 52,
+ "cost": 0,
+ "started_at": "2026-07-23T19:03:15.531Z",
+ "finished_at": "2026-07-23T19:04:07.707Z",
+ "queued_at": "2026-07-23T19:01:19.278Z",
+ "bytes": 3629,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The game is functional but lacks visual polish and some elements like the score display are not fully implemented.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 57,
+ "cost": 0,
+ "started_at": "2026-07-23T19:01:19.285Z",
+ "finished_at": "2026-07-23T19:02:16.171Z",
+ "queued_at": "2026-07-23T19:01:19.279Z",
+ "bytes": 10073,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The game is functional but lacks visual polish and some elements like the retro beep are not clearly implemented.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T19:04:47.263Z",
+ "aiPick": "qwen3-14b",
+ "voted_at": "2026-07-23T19:04:49.970Z"
+ },
+ {
+ "id": "a03b18e65a8c",
+ "title": "Free-Roster — DVD Bounce + Trails",
+ "prompt": "Build a single-file HTML page: the bouncing DVD logo, elevated. The logo changes color on each wall hit, corner-hits trigger a celebratory particle burst + counter, and it leaves a fading motion trail. Add-logo button for chaos. Canvas-based, self-contained, no external assets.",
+ "category": "Games",
+ "designTools": false,
+ "created_at": "2026-07-23T19:05:09.336Z",
+ "winner": "claude-code",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 34,
+ "cost": 0,
+ "started_at": "2026-07-23T19:05:09.364Z",
+ "finished_at": "2026-07-23T19:05:42.992Z",
+ "queued_at": "2026-07-23T19:05:09.339Z",
+ "bytes": 4761,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The logo is bouncing and changing color as required, but the particle burst and counter are missing.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 33,
+ "cost": 0,
+ "started_at": "2026-07-23T19:05:42.998Z",
+ "finished_at": "2026-07-23T19:06:16.437Z",
+ "queued_at": "2026-07-23T19:05:09.341Z",
+ "bytes": 4199,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is functional but lacks visual polish and detail.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 15,
+ "cost": 0,
+ "started_at": "2026-07-23T19:06:16.441Z",
+ "finished_at": "2026-07-23T19:06:31.101Z",
+ "queued_at": "2026-07-23T19:05:09.342Z",
+ "bytes": 3467,
+ "thumb": true,
+ "aiScore": 4.8,
+ "aiReason": "The page is empty and does not display any of the required elements or functionality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 3,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 3.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 27,
+ "cost": 0,
+ "started_at": "2026-07-23T19:05:09.366Z",
+ "finished_at": "2026-07-23T19:05:36.183Z",
+ "queued_at": "2026-07-23T19:05:09.344Z",
+ "bytes": 4006,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The logo is visible and moves correctly, but the color change upon wall hits isn't clear.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 58,
+ "cost": 0,
+ "started_at": "2026-07-23T19:06:31.103Z",
+ "finished_at": "2026-07-23T19:07:29.403Z",
+ "queued_at": "2026-07-23T19:05:09.346Z",
+ "bytes": 4011,
+ "thumb": true
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 53,
+ "cost": 0,
+ "started_at": "2026-07-23T19:05:09.369Z",
+ "finished_at": "2026-07-23T19:06:02.636Z",
+ "queued_at": "2026-07-23T19:05:09.362Z",
+ "bytes": 9751,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The model fulfills the challenge criteria and has good visual quality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
+ }
+ ],
+ "judged_at": "2026-07-23T19:08:04.693Z",
+ "judging": false,
+ "aiPick": "claude-code",
+ "voted_at": "2026-07-23T19:08:09.924Z"
+ },
+ {
+ "id": "43dfd86f6460",
+ "title": "Free-Roster — Analog Synth Pad",
+ "prompt": "Build a single-file HTML page: an XY synth pad. Dragging on a pad controls pitch (X) and filter cutoff (Y) of a WebAudio oscillator; waveform selector, delay toggle, and a visual of the current note. Self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T19:08:26.014Z",
+ "winner": "hf-qwen-coder-32b",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 28,
+ "cost": 0,
+ "started_at": "2026-07-23T19:08:26.058Z",
+ "finished_at": "2026-07-23T19:08:54.020Z",
+ "queued_at": "2026-07-23T19:08:26.028Z",
+ "bytes": 4123,
+ "thumb": true,
+ "aiScore": 6.5,
+ "aiReason": "The page is functional but lacks visual elements and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6
+ },
+ "aiSpread": 1
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 52,
+ "cost": 0,
+ "started_at": "2026-07-23T19:08:54.024Z",
+ "finished_at": "2026-07-23T19:09:46.524Z",
+ "queued_at": "2026-07-23T19:08:26.034Z",
+ "bytes": 4563,
+ "thumb": true,
+ "aiScore": 6.3,
+ "aiReason": "The page is functional but lacks visual elements and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 6,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 19,
+ "cost": 0,
+ "started_at": "2026-07-23T19:09:46.527Z",
+ "finished_at": "2026-07-23T19:10:05.741Z",
+ "queued_at": "2026-07-23T19:08:26.040Z",
+ "bytes": 2817,
+ "thumb": true,
+ "aiScore": 6.3,
+ "aiReason": "The page is functional but lacks visual appeal and user interaction elements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 6,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 15,
+ "cost": 0,
+ "started_at": "2026-07-23T19:08:26.061Z",
+ "finished_at": "2026-07-23T19:08:40.627Z",
+ "queued_at": "2026-07-23T19:08:26.045Z",
+ "bytes": 2579,
+ "thumb": true,
+ "aiScore": 3.3,
+ "aiReason": "The image is blank and does not show any content related to the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 0,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 6.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 49,
+ "cost": 0,
+ "started_at": "2026-07-23T19:10:05.745Z",
+ "finished_at": "2026-07-23T19:10:55.153Z",
+ "queued_at": "2026-07-23T19:08:26.050Z",
+ "bytes": 3065,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The page is functional and meets the challenge criteria but lacks visual appeal.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 67,
+ "cost": 0,
+ "started_at": "2026-07-23T19:08:26.063Z",
+ "finished_at": "2026-07-23T19:09:32.987Z",
+ "queued_at": "2026-07-23T19:08:26.053Z",
+ "bytes": 12623,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The page is functional but lacks visual elements and interactivity beyond the basic XY pad.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T19:11:38.654Z",
+ "aiPick": "hf-qwen-coder-32b",
+ "voted_at": "2026-07-23T19:11:46.875Z"
+ },
+ {
+ "id": "5dbb1c6732ae",
+ "title": "Free-Roster — Constellation Clock",
+ "prompt": "Build a single-file HTML page: a clock rendered as a live star map. Hour/minute/second hands are drawn as constellations of connected stars rotating over a twinkling night sky; digital time in a corner; a subtle shooting star now and then. Canvas-based, self-contained, no external assets.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T19:12:01.467Z",
+ "winner": "claude-code",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 34,
+ "cost": 0,
+ "started_at": "2026-07-23T19:12:01.502Z",
+ "finished_at": "2026-07-23T19:12:35.916Z",
+ "queued_at": "2026-07-23T19:12:01.475Z",
+ "bytes": 5141,
+ "thumb": true,
+ "aiScore": 3.3,
+ "aiReason": "The image is blank and does not display any clock or star map elements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 3,
+ "minicpm-v:latest": 3.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 44,
+ "cost": 0,
+ "started_at": "2026-07-23T19:12:35.923Z",
+ "finished_at": "2026-07-23T19:13:20.106Z",
+ "queued_at": "2026-07-23T19:12:01.480Z",
+ "bytes": 3535,
+ "thumb": true,
+ "aiScore": 7,
+ "aiReason": "The clock is visually represented as constellations, fulfilling the challenge's requirements. However, the shooting star effect and subtle twinkling stars are missing.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 22,
+ "cost": 0,
+ "started_at": "2026-07-23T19:13:20.113Z",
+ "finished_at": "2026-07-23T19:13:41.978Z",
+ "queued_at": "2026-07-23T19:12:01.485Z",
+ "bytes": 2519,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The page is functional but lacks the subtle shooting star and digital time display as required.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 14,
+ "cost": 0,
+ "started_at": "2026-07-23T19:12:01.505Z",
+ "finished_at": "2026-07-23T19:12:15.727Z",
+ "queued_at": "2026-07-23T19:12:01.490Z",
+ "bytes": 2192,
+ "thumb": true,
+ "aiScore": 5.8,
+ "aiReason": "The image is blank and does not fulfill the challenge requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 5,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 1.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 70,
+ "cost": 0,
+ "started_at": "2026-07-23T19:13:41.982Z",
+ "finished_at": "2026-07-23T19:14:51.796Z",
+ "queued_at": "2026-07-23T19:12:01.494Z",
+ "bytes": 4031,
+ "thumb": true,
+ "aiScore": 6.5,
+ "aiReason": "The clock is visually represented as stars and the time displayed digitally in a corner, fulfilling most of the challenge criteria.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6
+ },
+ "aiSpread": 1
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 110,
+ "cost": 0,
+ "started_at": "2026-07-23T19:12:01.509Z",
+ "finished_at": "2026-07-23T19:13:51.396Z",
+ "queued_at": "2026-07-23T19:12:01.498Z",
+ "bytes": 10775,
+ "thumb": true,
+ "aiScore": 8,
+ "aiReason": "The model effectively fulfills the challenge by using canvas for a live star map clock and achieves good visual quality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7
+ },
+ "aiSpread": 2
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T19:16:00.966Z",
+ "aiPick": "claude-code",
+ "voted_at": "2026-07-23T19:16:02.294Z"
+ },
+ {
+ "id": "78dd0efa6a47",
+ "title": "Free-Roster — Lava Lamp",
+ "prompt": "Build a single-file HTML page: a metaball lava lamp. Blobs rise and fall and merge with smooth metaball rendering and a warm gradient; a color-theme selector and a blob-count slider. Canvas-based, self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T19:18:06.437Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 52,
+ "cost": 0,
+ "started_at": "2026-07-23T19:18:06.458Z",
+ "finished_at": "2026-07-23T19:18:58.619Z",
+ "queued_at": "2026-07-23T19:18:06.441Z",
+ "bytes": 4301,
+ "thumb": true
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 83,
+ "cost": 0,
+ "started_at": "2026-07-23T19:18:58.623Z",
+ "finished_at": "2026-07-23T19:20:21.868Z",
+ "queued_at": "2026-07-23T19:18:06.445Z",
+ "bytes": 5338,
+ "thumb": true
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 39,
+ "cost": 0,
+ "started_at": "2026-07-23T19:20:21.872Z",
+ "finished_at": "2026-07-23T19:21:00.623Z",
+ "queued_at": "2026-07-23T19:18:06.449Z",
+ "bytes": 3207,
+ "thumb": true
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 37,
+ "cost": 0,
+ "started_at": "2026-07-23T19:18:06.462Z",
+ "finished_at": "2026-07-23T19:18:43.030Z",
+ "queued_at": "2026-07-23T19:18:06.452Z",
+ "bytes": 3465,
+ "thumb": true
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T19:21:00.625Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T19:18:06.454Z"
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 144,
+ "cost": 0,
+ "started_at": "2026-07-23T19:18:06.464Z",
+ "finished_at": "2026-07-23T19:20:30.191Z",
+ "queued_at": "2026-07-23T19:18:06.456Z",
+ "bytes": 11682,
+ "thumb": true
+ }
+ ]
+ },
+ {
+ "id": "65f12bbe7a82",
+ "title": "RENTV — Deal Flow Dashboard",
+ "prompt": "Build a single-file HTML page: a polished commercial-real-estate DEAL FLOW dashboard for RENTV.com (a CRE news + deals site) using REAL deal data. Render the deals as filterable cards — filter by transaction type and property type, sort by amount — with a top summary strip (total $ volume, deal count, average occupancy) and a data-dense, trustworthy dark CRE aesthetic. Embed this REAL data verbatim as a JS array, and synthesize ~15 more realistic entries in the SAME shape to fill the grid:\n[{\"title\": \"Gilbert, AZ Multifamily Community Refinanced with New Loan for $51.8 Mil\", \"txn_type\": \"Financing\", \"property_type\": \"Multifamily\", \"city\": \"Gilbert\", \"state\": \"AZ\", \"amount_label\": \"$51.8M\", \"size_label\": \"236 units \\u00b7 986 SF\", \"occupancy_pct\": 97, \"year_built\": 2025}, {\"title\": \"The Swig Company Obtains Refi on Office High-Rise in San Francisco\\u0092s Financial District\", \"txn_type\": \"Financing\", \"property_type\": \"Office\", \"city\": \"San Francisco\", \"state\": null, \"amount_label\": \"$95.0M\", \"size_label\": null, \"occupancy_pct\": null, \"year_built\": 1891}, {\"title\": \"Tailwind Investment Group Spends $65 Mil on Two Sacramento Res Properties\", \"txn_type\": \"Sale\", \"property_type\": \"Multifamily\", \"city\": \"Sacramento\", \"state\": null, \"amount_label\": \"$65.0M\", \"size_label\": \"419 units\", \"occupancy_pct\": 94, \"year_built\": null}, {\"title\": \"Fresno Multifamily Sale Works Out to $177k per Unit\", \"txn_type\": \"Sale\", \"property_type\": \"Multifamily\", \"city\": \"Fresno\", \"state\": null, \"amount_label\": \"$177,000\", \"size_label\": \"248 units \\u00b7 878 SF\", \"occupancy_pct\": null, \"year_built\": null}, {\"title\": \"Retail Portfolio in Downtown Long Beach Fetches $30 Mil\", \"txn_type\": \"Sale\", \"property_type\": \"Retail\", \"city\": \"Long Beach\", \"state\": null, \"amount_label\": \"$30.0M\", \"size_label\": \"4.8 acres\", \"occupancy_pct\": 78, \"year_built\": null}, {\"title\": \"Shorenstein Buys 77k sf Bay Area Office Building\", \"txn_type\": \"Sale\", \"property_type\": \"Office\", \"city\": \"Bay Area\", \"state\": null, \"amount_label\": null, \"size_label\": null, \"occupancy_pct\": 81, \"year_built\": null}]\nSelf-contained, no external libraries or network calls.",
+ "category": "Real Work",
+ "designTools": false,
+ "created_at": "2026-07-23T19:20:06.752Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "queued",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": null,
+ "finished_at": null,
+ "queued_at": "2026-07-23T19:20:06.756Z"
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "queued",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": null,
+ "finished_at": null,
+ "queued_at": "2026-07-23T19:20:06.760Z"
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "queued",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": null,
+ "finished_at": null,
+ "queued_at": "2026-07-23T19:20:06.764Z"
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T19:20:06.779Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T19:20:06.768Z"
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "queued",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": null,
+ "finished_at": null,
+ "queued_at": "2026-07-23T19:20:06.772Z"
+ },
+ {
+ "model": "claude-code",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T19:20:30.194Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T19:20:06.775Z"
}
]
}
diff --git a/data/landings/a7640ab6a2fa-claude-code.html b/data/landings/a7640ab6a2fa-claude-code.html
new file mode 100644
index 0000000..2f282b7
--- /dev/null
+++ b/data/landings/a7640ab6a2fa-claude-code.html
@@ -0,0 +1,342 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>Free-Roster — Spirograph — built by Claude Opus (Max plan) · Model Arena</title>
+<style>*{box-sizing:border-box}html,body{margin:0;height:100%;background:#0b0d10;color:#e6ecf2;font:14px/1.5 ui-monospace,Menlo,monospace}
+header{display:flex;align-items:center;gap:14px;padding:13px 20px;border-bottom:1px solid #1c2430;background:#0e1116}
+header .k{color:#7cf;font-weight:700;letter-spacing:2px;text-transform:uppercase;font-size:12px;white-space:nowrap}
+header h1{font-size:15px;margin:0;font-weight:600}header .by{color:#8a93a0;font-size:12px;margin-left:auto;white-space:nowrap}
+.wrap{height:calc(100% - 51px)}iframe{width:100%;height:100%;border:0;background:#000;display:block}</style></head><body>
+<header><span class="k">⚔ Model Arena</span><h1>Free-Roster — Spirograph</h1><span class="by">built by <b>Claude Opus (Max plan)</b></span></header>
+<div class="wrap"><iframe sandbox="allow-scripts allow-pointer-lock" srcdoc="<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Spirograph Studio</title>
+<style>
+ :root{
+ --bg:#0d0f1a; --panel:#161a2e; --panel2:#1e2340;
+ --ink:#e8ecff; --muted:#8b91b8; --accent:#5eead4; --accent2:#a78bfa;
+ --line:#2a3055;
+ }
+ *{box-sizing:border-box}
+ html,body{height:100%}
+ body{
+ margin:0; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
+ background:radial-gradient(1200px 800px at 70% -10%,#1b2145 0%,var(--bg) 55%);
+ color:var(--ink); display:flex; min-height:100%; overflow:hidden;
+ }
+ .wrap{display:flex; width:100%; height:100vh}
+ .stage{
+ flex:1; display:flex; align-items:center; justify-content:center;
+ position:relative; padding:24px;
+ }
+ #canvas{
+ background:#07080f;
+ border-radius:16px;
+ box-shadow:0 20px 60px rgba(0,0,0,.55), inset 0 0 0 1px rgba(255,255,255,.04);
+ max-width:100%; max-height:100%;
+ touch-action:none;
+ }
+ .panel{
+ width:320px; flex-shrink:0; background:linear-gradient(180deg,var(--panel),var(--panel2));
+ border-left:1px solid var(--line);
+ padding:22px 22px 26px; display:flex; flex-direction:column; gap:20px;
+ overflow-y:auto;
+ }
+ .brand{display:flex; align-items:center; gap:11px}
+ .brand .dot{
+ width:30px;height:30px;border-radius:9px;
+ background:conic-gradient(from 0deg,var(--accent),var(--accent2),#f472b6,var(--accent));
+ box-shadow:0 0 18px rgba(94,234,212,.4);
+ }
+ .brand h1{font-size:17px; margin:0; letter-spacing:.4px; font-weight:650}
+ .brand span{font-size:11px; color:var(--muted); display:block; margin-top:1px; letter-spacing:.5px}
+
+ .ctrl{display:flex; flex-direction:column; gap:8px}
+ .ctrl label{display:flex; justify-content:space-between; align-items:baseline; font-size:12.5px; color:var(--muted); letter-spacing:.3px}
+ .ctrl label b{color:var(--ink); font-weight:600; font-variant-numeric:tabular-nums; font-size:13px}
+ input[type=range]{
+ -webkit-appearance:none; appearance:none; width:100%; height:6px; border-radius:6px;
+ background:linear-gradient(90deg,var(--accent),var(--accent2)); outline:none; cursor:pointer;
+ }
+ input[type=range]::-webkit-slider-thumb{
+ -webkit-appearance:none; width:18px;height:18px;border-radius:50%;
+ background:#fff; border:3px solid var(--accent); box-shadow:0 2px 8px rgba(0,0,0,.5); cursor:pointer;
+ }
+ input[type=range]::-moz-range-thumb{
+ width:16px;height:16px;border-radius:50%; background:#fff; border:3px solid var(--accent); cursor:pointer;
+ }
+ .row{display:flex; gap:10px}
+ .toggle{
+ display:flex; align-items:center; justify-content:space-between; gap:10px;
+ font-size:12.5px; color:var(--muted); background:rgba(255,255,255,.03);
+ padding:10px 12px; border-radius:10px; border:1px solid var(--line);
+ }
+ .switch{position:relative; width:42px; height:23px; flex-shrink:0}
+ .switch input{opacity:0; width:0; height:0}
+ .slider-sw{
+ position:absolute; inset:0; background:#3a4066; border-radius:23px; transition:.2s; cursor:pointer;
+ }
+ .slider-sw:before{
+ content:""; position:absolute; height:17px; width:17px; left:3px; top:3px;
+ background:#fff; border-radius:50%; transition:.2s;
+ }
+ .switch input:checked + .slider-sw{background:linear-gradient(90deg,var(--accent),var(--accent2))}
+ .switch input:checked + .slider-sw:before{transform:translateX(19px)}
+
+ button{
+ flex:1; font:inherit; font-size:13px; font-weight:600; letter-spacing:.3px;
+ padding:12px 10px; border-radius:11px; border:1px solid var(--line);
+ background:rgba(255,255,255,.04); color:var(--ink); cursor:pointer; transition:.15s;
+ }
+ button:hover{background:rgba(255,255,255,.09); transform:translateY(-1px)}
+ button:active{transform:translateY(0)}
+ button.primary{
+ background:linear-gradient(90deg,var(--accent),var(--accent2)); color:#0b0d18; border:none;
+ }
+ button.primary:hover{filter:brightness(1.08)}
+ .foot{margin-top:auto; font-size:11px; color:var(--muted); line-height:1.5; letter-spacing:.2px}
+ .hint{font-size:11px; color:var(--muted); margin-top:-6px}
+ @media (max-width:760px){
+ body{overflow:auto}
+ .wrap{flex-direction:column; height:auto}
+ .panel{width:100%; border-left:none; border-top:1px solid var(--line)}
+ .stage{padding:16px; min-height:60vh}
+ }
+</style>
+</head>
+<body>
+<div class="wrap">
+ <div class="stage">
+ <canvas id="canvas" width="760" height="760"></canvas>
+ </div>
+
+ <aside class="panel">
+ <div class="brand">
+ <div class="dot"></div>
+ <div>
+ <h1>Spirograph Studio</h1>
+ <span>HYPOTROCHOID PLOTTER</span>
+ </div>
+ </div>
+
+ <div class="ctrl">
+ <label>Fixed gear radius <span>R</span> <b id="vR">140</b></label>
+ <input type="range" id="R" min="40" max="220" value="140" step="1">
+ </div>
+
+ <div class="ctrl">
+ <label>Rolling gear radius <span>r</span> <b id="vr">53</b></label>
+ <input type="range" id="r" min="8" max="150" value="53" step="1">
+ </div>
+
+ <div class="ctrl">
+ <label>Pen offset <span>d</span> <b id="vd">78</b></label>
+ <input type="range" id="d" min="0" max="150" value="78" step="1">
+ </div>
+
+ <div class="ctrl">
+ <label>Speed <b id="vs">6</b></label>
+ <input type="range" id="speed" min="1" max="24" value="6" step="1">
+ </div>
+
+ <div class="ctrl">
+ <label>Line width <b id="vw">1.6</b></label>
+ <input type="range" id="lw" min="1" max="6" value="1.6" step="0.1">
+ </div>
+
+ <div class="toggle">
+ <span>Color cycling</span>
+ <label class="switch"><input type="checkbox" id="cycle" checked><span class="slider-sw"></span></label>
+ </div>
+
+ <div class="toggle">
+ <span>Show gears</span>
+ <label class="switch"><input type="checkbox" id="gears" checked><span class="slider-sw"></span></label>
+ </div>
+
+ <div class="row">
+ <button id="clear">Clear</button>
+ <button id="save" class="primary">Save PNG</button>
+ </div>
+ <div class="hint">Adjust any slider and the curve redraws live. It draws until the pattern closes, then loops.</div>
+
+ <div class="foot">
+ Curve: x = (R−r)·cos t + d·cos((R−r)/r·t)<br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y = (R−r)·sin t − d·sin((R−r)/r·t)
+ </div>
+ </aside>
+</div>
+
+<script>
+(function(){
+ "use strict";
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const W = canvas.width, H = canvas.height;
+ const cx = W/2, cy = H/2;
+
+ // Controls
+ const el = id => document.getElementById(id);
+ const R = el('R'), r = el('r'), d = el('d'), speed = el('speed'), lw = el('lw');
+ const cycle = el('cycle'), gears = el('gears');
+ const vR=el('vR'), vr=el('vr'), vd=el('vd'), vs=el('vs'), vw=el('vw');
+
+ // Drawing state
+ let t = 0; // parameter
+ let hue = Math.random()*360;
+ let lastPt = null; // last plotted point on the persistent trail
+ let period = 0; // t value at which the curve closes
+ let running = true;
+
+ // Offscreen trail layer (persists between frames; gear overlay is drawn fresh each frame)
+ const trail = document.createElement('canvas');
+ trail.width = W; trail.height = H;
+ const tctx = trail.getContext('2d');
+
+ function gcd(a,b){ a=Math.round(a); b=Math.round(b); while(b){ [a,b]=[b,a%b]; } return a||1; }
+
+ function computePeriod(){
+ const rr = Math.max(1, parseInt(r.value,10));
+ const RR = parseInt(R.value,10);
+ // full pattern closes after r/gcd(R,r) revolutions of the driving angle
+ const revs = rr / gcd(RR, rr);
+ period = 2*Math.PI*revs;
+ }
+
+ function point(tt){
+ const RR = parseInt(R.value,10);
+ const rr = Math.max(1, parseInt(r.value,10));
+ const dd = parseInt(d.value,10);
+ const k = RR - rr;
+ const ratio = k / rr;
+ return {
+ x: cx + k*Math.cos(tt) + dd*Math.cos(ratio*tt),
+ y: cy + k*Math.sin(tt) - dd*Math.sin(ratio*tt)
+ };
+ }
+
+ function resetTrail(){
+ tctx.clearRect(0,0,W,H);
+ t = 0;
+ lastPt = point(0);
+ computePeriod();
+ }
+
+ function restart(){
+ // called when a shape parameter changes: fresh curve
+ hue = (hue + 47) % 360;
+ resetTrail();
+ render(); // paint immediately so change is visible even if paused between frames
+ }
+
+ // Draw the rolling-gear schematic for the current t (overlay only)
+ function drawGears(octx, tt){
+ const RR = parseInt(R.value,10);
+ const rr = Math.max(1, parseInt(r.value,10));
+ const dd = parseInt(d.value,10);
+ const k = RR - rr;
+ // fixed gear
+ octx.save();
+ octx.strokeStyle = 'rgba(255,255,255,.16)';
+ octx.lineWidth = 1;
+ octx.beginPath(); octx.arc(cx, cy, RR, 0, Math.PI*2); octx.stroke();
+ // rolling gear center
+ const gx = cx + k*Math.cos(tt);
+ const gy = cy + k*Math.sin(tt);
+ octx.strokeStyle = 'rgba(167,139,250,.55)';
+ octx.beginPath(); octx.arc(gx, gy, rr, 0, Math.PI*2); octx.stroke();
+ // pen position (= current curve point)
+ const p = point(tt);
+ octx.strokeStyle = 'rgba(94,234,212,.5)';
+ octx.beginPath(); octx.moveTo(gx,gy); octx.lineTo(p.x,p.y); octx.stroke();
+ octx.fillStyle = '#5eead4';
+ octx.beginPath(); octx.arc(p.x,p.y,3.2,0,Math.PI*2); octx.fill();
+ octx.fillStyle = 'rgba(167,139,250,.9)';
+ octx.beginPath(); octx.arc(gx,gy,2.5,0,Math.PI*2); octx.fill();
+ octx.restore();
+ }
+
+ function render(){
+ ctx.clearRect(0,0,W,H);
+ ctx.drawImage(trail,0,0);
+ if(gears.checked && running) drawGears(ctx, t);
+ }
+
+ function frame(){
+ if(running){
+ const steps = parseInt(speed.value,10);
+ const width = parseFloat(lw.value);
+ const dt = 0.012; // base angular step per micro-segment
+ tctx.lineCap = 'round';
+ tctx.lineJoin = 'round';
+ tctx.lineWidth = width;
+
+ for(let i=0;i<steps;i++){
+ const nt = t + dt;
+ const p = point(nt);
+ if(cycle.checked){
+ hue = (hue + 0.35) % 360;
+ tctx.strokeStyle = 'hsl('+hue.toFixed(1)+',85%,62%)';
+ } else {
+ tctx.strokeStyle = 'hsl('+hue.toFixed(1)+',85%,62%)';
+ }
+ tctx.beginPath();
+ tctx.moveTo(lastPt.x,lastPt.y);
+ tctx.lineTo(p.x,p.y);
+ tctx.stroke();
+ lastPt = p;
+ t = nt;
+ if(t >= period){
+ // pattern complete: keep it, then start a fresh overlaid pass with a new base hue
+ t = 0;
+ lastPt = point(0);
+ hue = (hue + 63) % 360;
+ }
+ }
+ }
+ render();
+ requestAnimationFrame(frame);
+ }
+
+ // Wire up value labels + change handlers
+ function bind(input, label, fmt){
+ const upd = ()=> label.textContent = fmt ? fmt(input.value) : input.value;
+ input.addEventListener('input', upd); upd();
+ return upd;
+ }
+ bind(R, vR);
+ bind(r, vr);
+ bind(d, vd);
+ bind(speed, vs);
+ bind(lw, vw);
+
+ // Shape-changing sliders restart the curve
+ [R,r,d].forEach(inp => inp.addEventListener('input', restart));
+
+ el('clear').addEventListener('click', ()=>{
+ restart();
+ });
+
+ el('save').addEventListener('click', ()=>{
+ // Composite onto a dark background so the PNG isn't transparent
+ const out = document.createElement('canvas');
+ out.width = W; out.height = H;
+ const octx = out.getContext('2d');
+ octx.fillStyle = '#07080f';
+ octx.fillRect(0,0,W,H);
+ octx.drawImage(trail,0,0);
+ const link = document.createElement('a');
+ link.download = 'spirograph-' + Date.now() + '.png';
+ link.href = out.toDataURL('image/png');
+ link.click();
+ });
+
+ // Init
+ resetTrail();
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>" title="Free-Roster — Spirograph"></iframe></div>
+</body></html>
\ No newline at end of file
diff --git a/data/votes.jsonl b/data/votes.jsonl
index 2751341..3ca9b7d 100644
--- a/data/votes.jsonl
+++ b/data/votes.jsonl
@@ -42,3 +42,10 @@
{"ts":"2026-07-23T18:40:48.567Z","challenge":"ebf4f7984098","title":"All-Models — Aquarium Sim","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code","kimi","gpt","grok"]}
{"ts":"2026-07-23T18:43:49.240Z","challenge":"0cb3fc06b069","title":"Free-Roster — Particle Text","winner":"qwen3-14b","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code"]}
{"ts":"2026-07-23T18:48:40.763Z","challenge":"1d4944290382","title":"Free-Roster — Isometric City","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code"]}
+{"ts":"2026-07-23T18:53:21.925Z","challenge":"68b40458a368","title":"Free-Roster — Rope Physics","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code"]}
+{"ts":"2026-07-23T18:57:28.251Z","challenge":"7993245ce013","title":"Free-Roster — Emoji Memory Game","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code"]}
+{"ts":"2026-07-23T19:01:03.331Z","challenge":"a7640ab6a2fa","title":"Free-Roster — Spirograph","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code"]}
+{"ts":"2026-07-23T19:04:49.970Z","challenge":"f0251ce85742","title":"Free-Roster — Pong 2P","winner":"qwen3-14b","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code"]}
+{"ts":"2026-07-23T19:08:09.924Z","challenge":"a03b18e65a8c","title":"Free-Roster — DVD Bounce + Trails","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code"]}
+{"ts":"2026-07-23T19:11:46.875Z","challenge":"43dfd86f6460","title":"Free-Roster — Analog Synth Pad","winner":"hf-qwen-coder-32b","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code"]}
+{"ts":"2026-07-23T19:16:02.294Z","challenge":"5dbb1c6732ae","title":"Free-Roster — Constellation Clock","winner":"claude-code","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","hf-qwen-coder-32b","claude-code"]}
diff --git a/star-clock.html b/star-clock.html
new file mode 100644
index 0000000..8ffc77f
--- /dev/null
+++ b/star-clock.html
@@ -0,0 +1,364 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Constellation Clock</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ overflow: hidden;
+ background: #05060f;
+ }
+ #sky {
+ display: block;
+ width: 100vw;
+ height: 100vh;
+ }
+ #digital {
+ position: fixed;
+ right: 22px;
+ bottom: 18px;
+ font-family: "Courier New", ui-monospace, monospace;
+ color: rgba(200, 220, 255, 0.85);
+ letter-spacing: 3px;
+ font-size: 26px;
+ text-shadow: 0 0 8px rgba(120,170,255,0.6), 0 0 18px rgba(80,120,255,0.35);
+ user-select: none;
+ z-index: 5;
+ }
+ #digital .date {
+ display: block;
+ font-size: 12px;
+ letter-spacing: 4px;
+ text-align: right;
+ margin-top: 4px;
+ color: rgba(150, 180, 230, 0.6);
+ }
+ #label {
+ position: fixed;
+ left: 22px;
+ top: 16px;
+ font-family: "Courier New", ui-monospace, monospace;
+ color: rgba(160, 190, 240, 0.5);
+ font-size: 12px;
+ letter-spacing: 4px;
+ text-transform: uppercase;
+ user-select: none;
+ z-index: 5;
+ }
+</style>
+</head>
+<body>
+<canvas id="sky"></canvas>
+<div id="label">Celestial Chronometer</div>
+<div id="digital"><span id="clock">00:00:00</span><span class="date" id="date"></span></div>
+
+<script>
+(function () {
+ "use strict";
+
+ const canvas = document.getElementById("sky");
+ const ctx = canvas.getContext("2d");
+ const clockEl = document.getElementById("clock");
+ const dateEl = document.getElementById("date");
+
+ let W = 0, H = 0, CX = 0, CY = 0, DPR = 1;
+
+ function resize() {
+ DPR = Math.min(window.devicePixelRatio || 1, 2);
+ W = window.innerWidth;
+ H = window.innerHeight;
+ canvas.width = Math.floor(W * DPR);
+ canvas.height = Math.floor(H * DPR);
+ canvas.style.width = W + "px";
+ canvas.style.height = H + "px";
+ ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
+ CX = W / 2;
+ CY = H / 2;
+ buildStars();
+ }
+
+ // ---------- Background starfield ----------
+ let bgStars = [];
+ function buildStars() {
+ bgStars = [];
+ const count = Math.round((W * H) / 5200);
+ for (let i = 0; i < count; i++) {
+ bgStars.push({
+ x: Math.random() * W,
+ y: Math.random() * H,
+ r: Math.random() * 1.3 + 0.2,
+ base: Math.random() * 0.5 + 0.2,
+ amp: Math.random() * 0.5 + 0.1,
+ speed: Math.random() * 2 + 0.5,
+ phase: Math.random() * Math.PI * 2,
+ hue: 210 + Math.random() * 40
+ });
+ }
+ }
+
+ function drawBackground(t) {
+ // subtle vertical nebula gradient
+ const g = ctx.createRadialGradient(CX, CY, 0, CX, CY, Math.max(W, H) * 0.75);
+ g.addColorStop(0, "#0a1230");
+ g.addColorStop(0.5, "#070a1c");
+ g.addColorStop(1, "#03040c");
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, W, H);
+
+ for (let i = 0; i < bgStars.length; i++) {
+ const s = bgStars[i];
+ const tw = s.base + s.amp * Math.sin(t * s.speed + s.phase);
+ const a = Math.max(0, Math.min(1, tw));
+ ctx.beginPath();
+ ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
+ ctx.fillStyle = "hsla(" + s.hue + ",70%,85%," + a + ")";
+ ctx.fill();
+ }
+ }
+
+ // ---------- Constellation hand definitions ----------
+ // Each hand is a chain of stars at given radius fractions along the hand.
+ // Points: {r: fraction of handRadius, o: sideways offset fraction, mag: star size}
+ const secondShape = [
+ { r: 0.00, o: 0.00, mag: 2.0 },
+ { r: 0.30, o: 0.05, mag: 1.6 },
+ { r: 0.55, o: -0.05, mag: 1.8 },
+ { r: 0.78, o: 0.04, mag: 1.5 },
+ { r: 0.95, o: 0.00, mag: 2.6 }
+ ];
+ const minuteShape = [
+ { r: 0.00, o: 0.00, mag: 2.4 },
+ { r: 0.22, o: 0.07, mag: 2.0 },
+ { r: 0.42, o: -0.06, mag: 2.2 },
+ { r: 0.60, o: 0.08, mag: 2.0 },
+ { r: 0.78, o: -0.04, mag: 2.4 },
+ { r: 0.92, o: 0.05, mag: 3.2 }
+ ];
+ const hourShape = [
+ { r: 0.00, o: 0.00, mag: 2.8 },
+ { r: 0.25, o: 0.10, mag: 2.4 },
+ { r: 0.30, o: -0.10, mag: 2.4 },
+ { r: 0.52, o: 0.06, mag: 2.6 },
+ { r: 0.70, o: -0.08, mag: 2.6 },
+ { r: 0.88, o: 0.00, mag: 3.6 }
+ ];
+
+ function drawConstellation(shape, angle, handRadius, color, glow, t, twinkle) {
+ // angle: 0 = 12 o'clock (up). rotate coordinate.
+ const ca = Math.cos(angle - Math.PI / 2);
+ const sa = Math.sin(angle - Math.PI / 2);
+ const pts = [];
+ for (let i = 0; i < shape.length; i++) {
+ const p = shape[i];
+ const along = p.r * handRadius;
+ const side = p.o * handRadius;
+ // local coords: along axis + perpendicular offset
+ const lx = along;
+ const ly = side;
+ const x = CX + lx * ca - ly * sa;
+ const y = CY + lx * sa + ly * ca;
+ pts.push({ x: x, y: y, mag: p.mag });
+ }
+
+ // connecting lines
+ ctx.save();
+ ctx.lineWidth = 1.2;
+ ctx.strokeStyle = color.line;
+ ctx.shadowColor = color.glow;
+ ctx.shadowBlur = glow;
+ ctx.beginPath();
+ ctx.moveTo(pts[0].x, pts[0].y);
+ for (let i = 1; i < pts.length; i++) ctx.lineTo(pts[i].x, pts[i].y);
+ ctx.stroke();
+ ctx.restore();
+
+ // stars
+ for (let i = 0; i < pts.length; i++) {
+ const p = pts[i];
+ const tw = twinkle ? (0.75 + 0.25 * Math.sin(t * 3 + i * 1.7)) : 1;
+ const rad = p.mag * tw;
+ ctx.save();
+ ctx.shadowColor = color.glow;
+ ctx.shadowBlur = glow * 1.4;
+ const grd = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, rad * 3);
+ grd.addColorStop(0, color.core);
+ grd.addColorStop(0.4, color.star);
+ grd.addColorStop(1, "rgba(0,0,0,0)");
+ ctx.fillStyle = grd;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, rad * 3, 0, Math.PI * 2);
+ ctx.fill();
+ // bright core
+ ctx.shadowBlur = 0;
+ ctx.fillStyle = color.core;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, rad * 0.8, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+ return pts;
+ }
+
+ // ---------- Hour ticks as faint stars around the dial ----------
+ function drawDialStars(t) {
+ const R = Math.min(W, H) * 0.44;
+ for (let i = 0; i < 12; i++) {
+ const a = (i / 12) * Math.PI * 2 - Math.PI / 2;
+ const x = CX + Math.cos(a) * R;
+ const y = CY + Math.sin(a) * R;
+ const tw = 0.5 + 0.5 * Math.sin(t * 1.5 + i);
+ const big = (i % 3 === 0);
+ ctx.beginPath();
+ ctx.arc(x, y, big ? 2.4 : 1.4, 0, Math.PI * 2);
+ ctx.fillStyle = "rgba(180,205,255," + (0.35 + 0.35 * tw) + ")";
+ ctx.shadowColor = "rgba(140,180,255,0.8)";
+ ctx.shadowBlur = big ? 10 : 5;
+ ctx.fill();
+ ctx.shadowBlur = 0;
+ }
+ // center star (the pole)
+ const cg = ctx.createRadialGradient(CX, CY, 0, CX, CY, 14);
+ cg.addColorStop(0, "rgba(255,255,255,0.95)");
+ cg.addColorStop(0.4, "rgba(180,210,255,0.6)");
+ cg.addColorStop(1, "rgba(0,0,0,0)");
+ ctx.fillStyle = cg;
+ ctx.beginPath();
+ ctx.arc(CX, CY, 14, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ // ---------- Shooting stars ----------
+ let shootings = [];
+ let nextShoot = 2 + Math.random() * 4;
+ function spawnShoot() {
+ const edge = Math.random();
+ const startX = Math.random() * W;
+ const startY = Math.random() * H * 0.5;
+ const angle = Math.PI * 0.15 + Math.random() * Math.PI * 0.2; // downward-right-ish
+ const speed = 500 + Math.random() * 400;
+ shootings.push({
+ x: startX,
+ y: startY,
+ vx: Math.cos(angle) * speed * (Math.random() < 0.5 ? 1 : -1),
+ vy: Math.sin(angle) * speed,
+ life: 0,
+ maxLife: 0.9 + Math.random() * 0.5,
+ len: 80 + Math.random() * 120
+ });
+ }
+ function updateShoot(dt) {
+ nextShoot -= dt;
+ if (nextShoot <= 0) {
+ spawnShoot();
+ nextShoot = 4 + Math.random() * 7;
+ }
+ for (let i = shootings.length - 1; i >= 0; i--) {
+ const s = shootings[i];
+ s.life += dt;
+ s.x += s.vx * dt;
+ s.y += s.vy * dt;
+ if (s.life > s.maxLife) shootings.splice(i, 1);
+ }
+ }
+ function drawShoot() {
+ for (let i = 0; i < shootings.length; i++) {
+ const s = shootings[i];
+ const prog = s.life / s.maxLife;
+ const alpha = Math.sin(prog * Math.PI); // fade in/out
+ const mag = Math.hypot(s.vx, s.vy) || 1;
+ const ux = s.vx / mag, uy = s.vy / mag;
+ const tx = s.x - ux * s.len;
+ const ty = s.y - uy * s.len;
+ const grd = ctx.createLinearGradient(s.x, s.y, tx, ty);
+ grd.addColorStop(0, "rgba(255,255,255," + (0.9 * alpha) + ")");
+ grd.addColorStop(1, "rgba(255,255,255,0)");
+ ctx.strokeStyle = grd;
+ ctx.lineWidth = 2;
+ ctx.shadowColor = "rgba(200,220,255,0.9)";
+ ctx.shadowBlur = 8;
+ ctx.beginPath();
+ ctx.moveTo(s.x, s.y);
+ ctx.lineTo(tx, ty);
+ ctx.stroke();
+ ctx.shadowBlur = 0;
+ // head
+ ctx.beginPath();
+ ctx.arc(s.x, s.y, 2, 0, Math.PI * 2);
+ ctx.fillStyle = "rgba(255,255,255," + alpha + ")";
+ ctx.fill();
+ }
+ }
+
+ // ---------- Main loop ----------
+ const colHour = {
+ line: "rgba(255,200,120,0.45)",
+ glow: "rgba(255,180,90,0.9)",
+ star: "rgba(255,190,110,0.9)",
+ core: "rgba(255,240,210,1)"
+ };
+ const colMinute = {
+ line: "rgba(140,200,255,0.5)",
+ glow: "rgba(120,180,255,0.9)",
+ star: "rgba(150,200,255,0.9)",
+ core: "rgba(235,245,255,1)"
+ };
+ const colSecond = {
+ line: "rgba(180,255,220,0.45)",
+ glow: "rgba(140,255,200,0.85)",
+ star: "rgba(170,255,210,0.9)",
+ core: "rgba(240,255,250,1)"
+ };
+
+ let lastTime = null;
+
+ function frame(now) {
+ if (lastTime === null) lastTime = now;
+ let dt = (now - lastTime) / 1000;
+ if (dt > 0.1) dt = 0.1;
+ lastTime = now;
+ const t = now / 1000;
+
+ const d = new Date();
+ const ms = d.getMilliseconds();
+ const sec = d.getSeconds() + ms / 1000;
+ const min = d.getMinutes() + sec / 60;
+ const hr = (d.getHours() % 12) + min / 60;
+
+ const secAngle = (sec / 60) * Math.PI * 2;
+ const minAngle = (min / 60) * Math.PI * 2;
+ const hrAngle = (hr / 12) * Math.PI * 2;
+
+ const base = Math.min(W, H);
+ const hourR = base * 0.20;
+ const minR = base * 0.33;
+ const secR = base * 0.40;
+
+ drawBackground(t);
+ drawDialStars(t);
+
+ updateShoot(dt);
+ drawShoot();
+
+ drawConstellation(hourShape, hrAngle, hourR, colHour, 16, t, true);
+ drawConstellation(minuteShape, minAngle, minR, colMinute, 14, t, true);
+ drawConstellation(secondShape, secAngle, secR, colSecond, 12, t, true);
+
+ // digital readout
+ const pad = (n) => (n < 10 ? "0" + n : "" + n);
+ clockEl.textContent = pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + pad(d.getSeconds());
+ dateEl.textContent = d.toLocaleDateString(undefined, { weekday: "short", month: "short", day: "numeric" });
+
+ requestAnimationFrame(frame);
+ }
+
+ window.addEventListener("resize", resize);
+ resize();
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
← 595106f arena: cost dashboard across top (today/week/month/year/all-
·
back to Model Arena
·
auto-save: 2026-07-23T12:51:13 (9 files) — data/challenges.j dad3461 →