← back to Model Arena
auto-save: 2026-07-23T09:50:03 (16 files) — data/challenges.json data/costlog.jsonl data/artifacts/1fd68f0d37b7/ data/artifacts/2ca024f41f3c/ data/artifacts/2e0c0d48120e/
9e98c9774ab0ff649da0b7cc35d08bd5da1d430d · 2026-07-23 09:50:13 -0700 · Steve Abrams
Files touched
A data/artifacts/1fd68f0d37b7/qwen25-7b.htmlA data/artifacts/1fd68f0d37b7/qwen25-7b.pngA data/artifacts/1fd68f0d37b7/qwen3-14b.htmlA data/artifacts/1fd68f0d37b7/qwen3-14b.pngA data/artifacts/2ca024f41f3c/claude-code.htmlA data/artifacts/2ca024f41f3c/claude-code.pngA data/artifacts/2ca024f41f3c/gemma3-12b.htmlA data/artifacts/2ca024f41f3c/gemma3-12b.pngA data/artifacts/2ca024f41f3c/gpt.htmlA data/artifacts/2ca024f41f3c/gpt.pngA data/artifacts/2ca024f41f3c/grok.htmlA data/artifacts/2ca024f41f3c/grok.pngA data/artifacts/2ca024f41f3c/hermes3-8b.htmlA data/artifacts/2ca024f41f3c/hermes3-8b.pngA data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.htmlA data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.pngA data/artifacts/2ca024f41f3c/kimi.htmlA data/artifacts/2ca024f41f3c/kimi.pngA data/artifacts/2ca024f41f3c/qwen25-7b.htmlA data/artifacts/2ca024f41f3c/qwen25-7b.pngA data/artifacts/2ca024f41f3c/qwen3-14b.htmlA data/artifacts/2ca024f41f3c/qwen3-14b.pngA data/artifacts/2e0c0d48120e/gemma3-12b.htmlA data/artifacts/2e0c0d48120e/gemma3-12b.pngA data/artifacts/2e0c0d48120e/hermes3-8b.htmlA data/artifacts/2e0c0d48120e/hermes3-8b.pngA data/artifacts/2e0c0d48120e/qwen3-14b.htmlA data/artifacts/2e0c0d48120e/qwen3-14b.pngA data/artifacts/6acc7c672157/claude-code.htmlA data/artifacts/6acc7c672157/claude-code.pngA data/artifacts/6acc7c672157/gemma3-12b.htmlA data/artifacts/6acc7c672157/gemma3-12b.pngA data/artifacts/6acc7c672157/gpt.htmlA data/artifacts/6acc7c672157/gpt.pngA data/artifacts/6acc7c672157/grok.htmlA data/artifacts/6acc7c672157/grok.pngA data/artifacts/6acc7c672157/hermes3-8b.htmlA data/artifacts/6acc7c672157/hermes3-8b.pngA data/artifacts/6acc7c672157/hf-qwen-coder-32b.htmlA data/artifacts/6acc7c672157/hf-qwen-coder-32b.pngA data/artifacts/6acc7c672157/kimi.htmlA data/artifacts/6acc7c672157/kimi.pngA data/artifacts/6acc7c672157/qwen25-7b.htmlA data/artifacts/6acc7c672157/qwen25-7b.pngA data/artifacts/6acc7c672157/qwen3-14b.htmlA data/artifacts/6acc7c672157/qwen3-14b.pngA data/artifacts/789f61c347ae/gemma3-12b.htmlA data/artifacts/789f61c347ae/gemma3-12b.pngA data/artifacts/789f61c347ae/hermes3-8b.htmlA data/artifacts/789f61c347ae/hermes3-8b.pngA data/artifacts/789f61c347ae/qwen3-14b.htmlA data/artifacts/789f61c347ae/qwen3-14b.pngA data/artifacts/78fea53364f0/gemma3-12b.htmlA data/artifacts/78fea53364f0/gemma3-12b.pngA data/artifacts/78fea53364f0/hermes3-8b.htmlA data/artifacts/78fea53364f0/hermes3-8b.pngA data/artifacts/a353bd10ea1b/gemma3-12b.htmlA data/artifacts/a353bd10ea1b/gemma3-12b.pngA data/artifacts/a353bd10ea1b/hermes3-8b.htmlA data/artifacts/a353bd10ea1b/hermes3-8b.pngA data/artifacts/a353bd10ea1b/qwen3-14b.htmlA data/artifacts/a353bd10ea1b/qwen3-14b.pngA data/artifacts/c21d462d6163/gemma3-12b.htmlA data/artifacts/c21d462d6163/gemma3-12b.pngA data/artifacts/c21d462d6163/hermes3-8b.htmlA data/artifacts/c21d462d6163/hermes3-8b.pngA data/artifacts/c21d462d6163/qwen3-14b.htmlA data/artifacts/c21d462d6163/qwen3-14b.pngA data/artifacts/c2d8bc75e725/claude-code.htmlA data/artifacts/c2d8bc75e725/claude-code.pngA data/artifacts/c2d8bc75e725/gemma3-12b.htmlA data/artifacts/c2d8bc75e725/gemma3-12b.pngA data/artifacts/c2d8bc75e725/gpt.htmlA data/artifacts/c2d8bc75e725/gpt.pngA data/artifacts/c2d8bc75e725/grok.htmlA data/artifacts/c2d8bc75e725/grok.pngA data/artifacts/c2d8bc75e725/hermes3-8b.htmlA data/artifacts/c2d8bc75e725/hermes3-8b.pngA data/artifacts/c2d8bc75e725/hf-qwen-coder-32b.htmlA data/artifacts/c2d8bc75e725/hf-qwen-coder-32b.pngA data/artifacts/c2d8bc75e725/kimi.htmlA data/artifacts/c2d8bc75e725/kimi.pngA data/artifacts/c2d8bc75e725/qwen25-7b.htmlA data/artifacts/c2d8bc75e725/qwen25-7b.pngA data/artifacts/c2d8bc75e725/qwen3-14b.htmlA data/artifacts/c2d8bc75e725/qwen3-14b.pngA data/artifacts/edbe5de71e26/claude-code.htmlA data/artifacts/edbe5de71e26/claude-code.pngA data/artifacts/edbe5de71e26/gemma3-12b.htmlA data/artifacts/edbe5de71e26/gemma3-12b.pngA data/artifacts/edbe5de71e26/gpt.htmlA data/artifacts/edbe5de71e26/gpt.pngA data/artifacts/edbe5de71e26/grok.htmlA data/artifacts/edbe5de71e26/grok.pngA data/artifacts/edbe5de71e26/hermes3-8b.htmlA data/artifacts/edbe5de71e26/hermes3-8b.pngA data/artifacts/edbe5de71e26/hf-qwen-coder-32b.htmlA data/artifacts/edbe5de71e26/hf-qwen-coder-32b.pngA data/artifacts/edbe5de71e26/kimi.htmlA data/artifacts/edbe5de71e26/kimi.pngA data/artifacts/edbe5de71e26/qwen25-7b.htmlA data/artifacts/edbe5de71e26/qwen25-7b.pngA data/artifacts/edbe5de71e26/qwen3-14b.htmlA data/artifacts/edbe5de71e26/qwen3-14b.pngA data/artifacts/f1756a933e20/claude-code.htmlA data/artifacts/f1756a933e20/claude-code.pngA data/artifacts/f1756a933e20/gemma3-12b.htmlA data/artifacts/f1756a933e20/gemma3-12b.pngA data/artifacts/f1756a933e20/gpt.htmlA data/artifacts/f1756a933e20/gpt.pngA data/artifacts/f1756a933e20/grok.htmlA data/artifacts/f1756a933e20/grok.pngA data/artifacts/f1756a933e20/hermes3-8b.htmlA data/artifacts/f1756a933e20/hermes3-8b.pngA data/artifacts/f1756a933e20/hf-qwen-coder-32b.htmlA data/artifacts/f1756a933e20/hf-qwen-coder-32b.pngA data/artifacts/f1756a933e20/kimi.htmlA data/artifacts/f1756a933e20/kimi.pngA data/artifacts/f1756a933e20/qwen25-7b.htmlA data/artifacts/f1756a933e20/qwen25-7b.pngA data/artifacts/f1756a933e20/qwen3-14b.htmlA data/artifacts/f1756a933e20/qwen3-14b.pngM data/challenges.jsonM data/costlog.jsonl
Diff
commit 9e98c9774ab0ff649da0b7cc35d08bd5da1d430d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 23 09:50:13 2026 -0700
auto-save: 2026-07-23T09:50:03 (16 files) — data/challenges.json data/costlog.jsonl data/artifacts/1fd68f0d37b7/ data/artifacts/2ca024f41f3c/ data/artifacts/2e0c0d48120e/
---
data/artifacts/1fd68f0d37b7/qwen25-7b.html | 84 ++
data/artifacts/1fd68f0d37b7/qwen25-7b.png | Bin 0 -> 4589 bytes
data/artifacts/1fd68f0d37b7/qwen3-14b.html | 115 ++
data/artifacts/1fd68f0d37b7/qwen3-14b.png | Bin 0 -> 4678 bytes
data/artifacts/2ca024f41f3c/claude-code.html | 305 +++++
data/artifacts/2ca024f41f3c/claude-code.png | Bin 0 -> 65178 bytes
data/artifacts/2ca024f41f3c/gemma3-12b.html | 119 ++
data/artifacts/2ca024f41f3c/gemma3-12b.png | Bin 0 -> 12532 bytes
data/artifacts/2ca024f41f3c/gpt.html | 324 +++++
data/artifacts/2ca024f41f3c/gpt.png | Bin 0 -> 36953 bytes
data/artifacts/2ca024f41f3c/grok.html | 355 ++++++
data/artifacts/2ca024f41f3c/grok.png | Bin 0 -> 186626 bytes
data/artifacts/2ca024f41f3c/hermes3-8b.html | 157 +++
data/artifacts/2ca024f41f3c/hermes3-8b.png | Bin 0 -> 7254 bytes
data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.html | 97 ++
data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.png | Bin 0 -> 56326 bytes
data/artifacts/2ca024f41f3c/kimi.html | 257 ++++
data/artifacts/2ca024f41f3c/kimi.png | Bin 0 -> 197793 bytes
data/artifacts/2ca024f41f3c/qwen25-7b.html | 92 ++
data/artifacts/2ca024f41f3c/qwen25-7b.png | Bin 0 -> 25392 bytes
data/artifacts/2ca024f41f3c/qwen3-14b.html | 196 +++
data/artifacts/2ca024f41f3c/qwen3-14b.png | Bin 0 -> 72986 bytes
data/artifacts/2e0c0d48120e/gemma3-12b.html | 108 ++
data/artifacts/2e0c0d48120e/gemma3-12b.png | Bin 0 -> 2724 bytes
data/artifacts/2e0c0d48120e/hermes3-8b.html | 83 ++
data/artifacts/2e0c0d48120e/hermes3-8b.png | Bin 0 -> 48703 bytes
data/artifacts/2e0c0d48120e/qwen3-14b.html | 101 ++
data/artifacts/2e0c0d48120e/qwen3-14b.png | Bin 0 -> 26052 bytes
data/artifacts/6acc7c672157/claude-code.html | 321 +++++
data/artifacts/6acc7c672157/claude-code.png | Bin 0 -> 216268 bytes
data/artifacts/6acc7c672157/gemma3-12b.html | 150 +++
data/artifacts/6acc7c672157/gemma3-12b.png | Bin 0 -> 96433 bytes
data/artifacts/6acc7c672157/gpt.html | 336 +++++
data/artifacts/6acc7c672157/gpt.png | Bin 0 -> 217974 bytes
data/artifacts/6acc7c672157/grok.html | 213 ++++
data/artifacts/6acc7c672157/grok.png | Bin 0 -> 229860 bytes
data/artifacts/6acc7c672157/hermes3-8b.html | 170 +++
data/artifacts/6acc7c672157/hermes3-8b.png | Bin 0 -> 2724 bytes
data/artifacts/6acc7c672157/hf-qwen-coder-32b.html | 112 ++
data/artifacts/6acc7c672157/hf-qwen-coder-32b.png | Bin 0 -> 40657 bytes
data/artifacts/6acc7c672157/kimi.html | 250 ++++
data/artifacts/6acc7c672157/kimi.png | Bin 0 -> 202487 bytes
data/artifacts/6acc7c672157/qwen25-7b.html | 119 ++
data/artifacts/6acc7c672157/qwen25-7b.png | Bin 0 -> 2724 bytes
data/artifacts/6acc7c672157/qwen3-14b.html | 143 +++
data/artifacts/6acc7c672157/qwen3-14b.png | Bin 0 -> 28053 bytes
data/artifacts/789f61c347ae/gemma3-12b.html | 135 ++
data/artifacts/789f61c347ae/gemma3-12b.png | Bin 0 -> 6023 bytes
data/artifacts/789f61c347ae/hermes3-8b.html | 175 +++
data/artifacts/789f61c347ae/hermes3-8b.png | Bin 0 -> 5647 bytes
data/artifacts/789f61c347ae/qwen3-14b.html | 208 ++++
data/artifacts/789f61c347ae/qwen3-14b.png | Bin 0 -> 13996 bytes
data/artifacts/78fea53364f0/gemma3-12b.html | 231 ++++
data/artifacts/78fea53364f0/gemma3-12b.png | Bin 0 -> 10947 bytes
data/artifacts/78fea53364f0/hermes3-8b.html | 198 +++
data/artifacts/78fea53364f0/hermes3-8b.png | Bin 0 -> 7098 bytes
data/artifacts/a353bd10ea1b/gemma3-12b.html | 127 ++
data/artifacts/a353bd10ea1b/gemma3-12b.png | Bin 0 -> 7671 bytes
data/artifacts/a353bd10ea1b/hermes3-8b.html | 119 ++
data/artifacts/a353bd10ea1b/hermes3-8b.png | Bin 0 -> 6764 bytes
data/artifacts/a353bd10ea1b/qwen3-14b.html | 155 +++
data/artifacts/a353bd10ea1b/qwen3-14b.png | Bin 0 -> 6090 bytes
data/artifacts/c21d462d6163/gemma3-12b.html | 141 +++
data/artifacts/c21d462d6163/gemma3-12b.png | Bin 0 -> 22819 bytes
data/artifacts/c21d462d6163/hermes3-8b.html | 122 ++
data/artifacts/c21d462d6163/hermes3-8b.png | Bin 0 -> 16630 bytes
data/artifacts/c21d462d6163/qwen3-14b.html | 146 +++
data/artifacts/c21d462d6163/qwen3-14b.png | Bin 0 -> 2728 bytes
data/artifacts/c2d8bc75e725/claude-code.html | 360 ++++++
data/artifacts/c2d8bc75e725/claude-code.png | Bin 0 -> 53267 bytes
data/artifacts/c2d8bc75e725/gemma3-12b.html | 158 +++
data/artifacts/c2d8bc75e725/gemma3-12b.png | Bin 0 -> 20536 bytes
data/artifacts/c2d8bc75e725/gpt.html | 387 ++++++
data/artifacts/c2d8bc75e725/gpt.png | Bin 0 -> 46726 bytes
data/artifacts/c2d8bc75e725/grok.html | 331 +++++
data/artifacts/c2d8bc75e725/grok.png | Bin 0 -> 49220 bytes
data/artifacts/c2d8bc75e725/hermes3-8b.html | 149 +++
data/artifacts/c2d8bc75e725/hermes3-8b.png | Bin 0 -> 37609 bytes
data/artifacts/c2d8bc75e725/hf-qwen-coder-32b.html | 85 ++
data/artifacts/c2d8bc75e725/hf-qwen-coder-32b.png | Bin 0 -> 10877 bytes
data/artifacts/c2d8bc75e725/kimi.html | 259 ++++
data/artifacts/c2d8bc75e725/kimi.png | Bin 0 -> 76511 bytes
data/artifacts/c2d8bc75e725/qwen25-7b.html | 59 +
data/artifacts/c2d8bc75e725/qwen25-7b.png | Bin 0 -> 18047 bytes
data/artifacts/c2d8bc75e725/qwen3-14b.html | 149 +++
data/artifacts/c2d8bc75e725/qwen3-14b.png | Bin 0 -> 20032 bytes
data/artifacts/edbe5de71e26/claude-code.html | 245 ++++
data/artifacts/edbe5de71e26/claude-code.png | Bin 0 -> 17587 bytes
data/artifacts/edbe5de71e26/gemma3-12b.html | 209 ++++
data/artifacts/edbe5de71e26/gemma3-12b.png | Bin 0 -> 3849 bytes
data/artifacts/edbe5de71e26/gpt.html | 378 ++++++
data/artifacts/edbe5de71e26/gpt.png | Bin 0 -> 5227 bytes
data/artifacts/edbe5de71e26/grok.html | 538 ++++++++
data/artifacts/edbe5de71e26/grok.png | Bin 0 -> 6935 bytes
data/artifacts/edbe5de71e26/hermes3-8b.html | 123 ++
data/artifacts/edbe5de71e26/hermes3-8b.png | Bin 0 -> 10288 bytes
data/artifacts/edbe5de71e26/hf-qwen-coder-32b.html | 224 ++++
data/artifacts/edbe5de71e26/hf-qwen-coder-32b.png | Bin 0 -> 3893 bytes
data/artifacts/edbe5de71e26/kimi.html | 364 ++++++
data/artifacts/edbe5de71e26/kimi.png | Bin 0 -> 5222 bytes
data/artifacts/edbe5de71e26/qwen25-7b.html | 106 ++
data/artifacts/edbe5de71e26/qwen25-7b.png | Bin 0 -> 3919 bytes
data/artifacts/edbe5de71e26/qwen3-14b.html | 207 ++++
data/artifacts/edbe5de71e26/qwen3-14b.png | Bin 0 -> 3865 bytes
data/artifacts/f1756a933e20/claude-code.html | 337 +++++
data/artifacts/f1756a933e20/claude-code.png | Bin 0 -> 140617 bytes
data/artifacts/f1756a933e20/gemma3-12b.html | 153 +++
data/artifacts/f1756a933e20/gemma3-12b.png | Bin 0 -> 11530 bytes
data/artifacts/f1756a933e20/gpt.html | 344 +++++
data/artifacts/f1756a933e20/gpt.png | Bin 0 -> 172614 bytes
data/artifacts/f1756a933e20/grok.html | 376 ++++++
data/artifacts/f1756a933e20/grok.png | Bin 0 -> 84568 bytes
data/artifacts/f1756a933e20/hermes3-8b.html | 83 ++
data/artifacts/f1756a933e20/hermes3-8b.png | Bin 0 -> 14129 bytes
data/artifacts/f1756a933e20/hf-qwen-coder-32b.html | 91 ++
data/artifacts/f1756a933e20/hf-qwen-coder-32b.png | Bin 0 -> 12170 bytes
data/artifacts/f1756a933e20/kimi.html | 264 ++++
data/artifacts/f1756a933e20/kimi.png | Bin 0 -> 176123 bytes
data/artifacts/f1756a933e20/qwen25-7b.html | 67 +
data/artifacts/f1756a933e20/qwen25-7b.png | Bin 0 -> 16951 bytes
data/artifacts/f1756a933e20/qwen3-14b.html | 156 +++
data/artifacts/f1756a933e20/qwen3-14b.png | Bin 0 -> 55859 bytes
data/challenges.json | 1309 +++++++++++++++++++-
data/costlog.jsonl | 15 +
124 files changed, 13478 insertions(+), 12 deletions(-)
diff --git a/data/artifacts/1fd68f0d37b7/qwen25-7b.html b/data/artifacts/1fd68f0d37b7/qwen25-7b.html
new file mode 100644
index 0000000..f31ccf3
--- /dev/null
+++ b/data/artifacts/1fd68f0d37b7/qwen25-7b.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Microphone Visualizer</title>
+ <style>
+ body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #282c34; color: white; }
+ canvas { margin-top: 20px; border: 1px solid white; }
+ .controls { margin-top: 20px; display: flex; gap: 10px; }
+ button { padding: 5px 10px; background-color: #6272a4; color: white; border: none; cursor: pointer; }
+ </style>
+</head>
+<body>
+ <canvas id="visualizer"></canvas>
+ <div class="controls">
+ <button onclick="toggleTheme()">Toggle Theme</button>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('visualizer');
+ let ctx;
+ let audioContext;
+ let sourceNode;
+ let analyserNode;
+ let dataArray;
+ let colorScheme = 'light';
+
+ function initAudio() {
+ if (!navigator.mediaDevices.getUserMedia) return;
+
+ navigator.mediaDevices.getUserMedia({ audio: true })
+ .then(stream => {
+ audioContext = new AudioContext();
+ sourceNode = audioContext.createMediaStreamSource(stream);
+ analyserNode = audioContext.createAnalyser();
+
+ sourceNode.connect(analyserNode);
+ analyserNode.fftSize = 256;
+ dataArray = new Uint8Array(analyserNode.frequencyBinCount);
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ requestAnimationFrame(drawVisualizer);
+ })
+ .catch(err => {
+ console.error('Error accessing microphone:', err);
+ document.body.textContent = 'Microphone access denied.';
+ });
+ }
+
+ function drawVisualizer() {
+ if (!analyserNode) return;
+
+ analyserNode.getByteFrequencyData(dataArray);
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ const barWidth = (canvas.width / dataArray.length) * 2;
+ let x = 0;
+
+ for (let i = 0; i < dataArray.length; i++) {
+ const barHeight = dataArray[i] / 4;
+ ctx.fillStyle = colorScheme === 'light' ? '#6272a4' : '#1e293b';
+ ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
+ x += barWidth + 5;
+ }
+
+ requestAnimationFrame(drawVisualizer);
+
+ const waveContext = canvas.getContext('2d');
+ waveContext.drawImage(analyserNode, 0, 0, canvas.width, canvas.height / 2);
+ }
+
+ function toggleTheme() {
+ colorScheme = colorScheme === 'light' ? 'dark' : 'light';
+ document.body.classList.toggle('light-theme');
+ document.body.classList.toggle('dark-theme');
+ ctx.fillStyle = colorScheme === 'light' ? '#6272a4' : '#1e293b';
+ }
+
+ initAudio();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/1fd68f0d37b7/qwen25-7b.png b/data/artifacts/1fd68f0d37b7/qwen25-7b.png
new file mode 100644
index 0000000..a833ee8
Binary files /dev/null and b/data/artifacts/1fd68f0d37b7/qwen25-7b.png differ
diff --git a/data/artifacts/1fd68f0d37b7/qwen3-14b.html b/data/artifacts/1fd68f0d37b7/qwen3-14b.html
new file mode 100644
index 0000000..6e61f14
--- /dev/null
+++ b/data/artifacts/1fd68f0d37b7/qwen3-14b.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Audio Visualizer</title>
+ <style>
+ body {
+ margin: 0;
+ background: #111;
+ color: #fff;
+ font-family: sans-serif;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ overflow: hidden;
+ }
+ canvas {
+ display: block;
+ margin: 0 auto;
+ background: #000;
+ }
+ #toggle {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ padding: 8px 16px;
+ background: #333;
+ border: none;
+ color: #fff;
+ cursor: pointer;
+ border-radius: 4px;
+ }
+ #message {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+ font-size: 1.2em;
+ opacity: 0;
+ transition: opacity 0.5s;
+ }
+ #message.visible {
+ opacity: 1;
+ }
+ </style>
+</head>
+<body>
+ <button id="toggle">Toggle Theme</button>
+ <canvas id="canvas" width="800" height="300"></canvas>
+ <div id="message">Microphone access denied. Please allow access to use the visualizer.</div>
+
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const toggleBtn = document.getElementById('toggle');
+ const message = document.getElementById('message');
+ let isDarkTheme = true;
+
+ let audioCtx;
+ let analyser;
+ let source;
+ let dataArray;
+
+ function initAudio() {
+ audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ analyser = audioCtx.createAnalyser();
+ analyser.fftSize = 256;
+ const bufferLength = analyser.frequencyBinCount;
+ dataArray = new Uint8Array(bufferLength);
+
+ navigator.mediaDevices.getUserMedia({ audio: true })
+ .then(stream => {
+ source = audioCtx.createMediaStreamSource(stream);
+ source.connect(analyser);
+ draw();
+ })
+ .catch(err => {
+ message.classList.add('visible');
+ console.error('Microphone access denied:', err);
+ });
+ }
+
+ function draw() {
+ requestAnimationFrame(draw);
+ analyser.getByteFrequencyData(dataArray);
+ const width = canvas.width;
+ const height = canvas.height;
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, width, height);
+
+ const barWidth = (width / dataArray.length) * 2.5;
+ let x = 0;
+
+ for (let i = 0; i < dataArray.length; i++) {
+ const barHeight = dataArray[i];
+ const bar = barHeight * 2.5;
+
+ ctx.fillStyle = isDarkTheme ? `rgb(${barHeight}, ${barHeight}, ${255 - barHeight})` : `rgb(${255 - barHeight}, ${barHeight}, ${barHeight})`;
+ ctx.fillRect(x, height - bar, barWidth, bar);
+ x += barWidth + 1;
+ }
+ }
+
+ toggleBtn.addEventListener('click', () => {
+ isDarkTheme = !isDarkTheme;
+ toggleBtn.textContent = isDarkTheme ? 'Toggle Theme' : 'Toggle Theme';
+ });
+
+ initAudio();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/1fd68f0d37b7/qwen3-14b.png b/data/artifacts/1fd68f0d37b7/qwen3-14b.png
new file mode 100644
index 0000000..f29ed55
Binary files /dev/null and b/data/artifacts/1fd68f0d37b7/qwen3-14b.png differ
diff --git a/data/artifacts/2ca024f41f3c/claude-code.html b/data/artifacts/2ca024f41f3c/claude-code.html
new file mode 100644
index 0000000..331403b
--- /dev/null
+++ b/data/artifacts/2ca024f41f3c/claude-code.html
@@ -0,0 +1,305 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Solar System Orrery</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ background: #05060f;
+ overflow: hidden;
+ font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ color: #dfe6ff;
+ }
+ #wrap { position: relative; width: 100vw; height: 100vh; }
+ canvas { display: block; width: 100%; height: 100%; cursor: default; }
+ #controls {
+ position: absolute;
+ left: 50%;
+ bottom: 18px;
+ transform: translateX(-50%);
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ background: rgba(10, 14, 30, 0.72);
+ border: 1px solid rgba(120, 150, 255, 0.25);
+ border-radius: 999px;
+ padding: 10px 18px;
+ backdrop-filter: blur(6px);
+ user-select: none;
+ }
+ #controls label { font-size: 13px; letter-spacing: 0.04em; opacity: 0.85; white-space: nowrap; }
+ #speed { width: 160px; accent-color: #6ea8ff; cursor: pointer; }
+ #speedVal { font-size: 12px; min-width: 42px; text-align: right; font-variant-numeric: tabular-nums; opacity: 0.8; }
+ #pauseBtn {
+ background: #1d2b55;
+ color: #dfe6ff;
+ border: 1px solid rgba(120, 150, 255, 0.4);
+ border-radius: 999px;
+ padding: 6px 16px;
+ font-size: 13px;
+ cursor: pointer;
+ min-width: 84px;
+ transition: background 0.15s;
+ }
+ #pauseBtn:hover { background: #2a3c74; }
+ #tooltip {
+ position: absolute;
+ pointer-events: none;
+ background: rgba(8, 12, 28, 0.9);
+ border: 1px solid rgba(140, 170, 255, 0.45);
+ border-radius: 6px;
+ padding: 4px 10px;
+ font-size: 13px;
+ letter-spacing: 0.03em;
+ white-space: nowrap;
+ transform: translate(-50%, -140%);
+ opacity: 0;
+ transition: opacity 0.12s;
+ }
+ #title {
+ position: absolute;
+ top: 16px;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 13px;
+ letter-spacing: 0.35em;
+ text-transform: uppercase;
+ opacity: 0.55;
+ user-select: none;
+ }
+</style>
+</head>
+<body>
+<div id="wrap">
+ <canvas id="cv"></canvas>
+ <div id="title">Solar System Orrery</div>
+ <div id="tooltip"></div>
+ <div id="controls">
+ <button id="pauseBtn">Pause</button>
+ <label for="speed">Speed</label>
+ <input type="range" id="speed" min="0" max="300" value="100">
+ <span id="speedVal">1.0×</span>
+ </div>
+</div>
+<script>
+(function () {
+ "use strict";
+ var canvas = document.getElementById("cv");
+ var ctx = canvas.getContext("2d");
+ var tooltip = document.getElementById("tooltip");
+ var pauseBtn = document.getElementById("pauseBtn");
+ var speedSlider = document.getElementById("speed");
+ var speedVal = document.getElementById("speedVal");
+
+ var W = 0, H = 0, DPR = 1, cx = 0, cy = 0, scale = 1;
+
+ // Orbital periods in Earth years (proportional speeds), display radii/sizes stylized.
+ var planets = [
+ { name: "Mercury", period: 0.241, dist: 0.10, size: 3.2, color: "#b8a89a" },
+ { name: "Venus", period: 0.615, dist: 0.155, size: 5.2, color: "#e8c47a" },
+ { name: "Earth", period: 1.000, dist: 0.215, size: 5.6, color: "#5f9ce0" },
+ { name: "Mars", period: 1.881, dist: 0.275, size: 4.2, color: "#d1663d" },
+ { name: "Jupiter", period: 11.86, dist: 0.40, size: 12, color: "#d9a878" },
+ { name: "Saturn", period: 29.46, dist: 0.53, size: 10, color: "#e3cf9a", ring: true },
+ { name: "Uranus", period: 84.01, dist: 0.65, size: 7, color: "#9fd8de" },
+ { name: "Neptune", period: 164.8, dist: 0.76, size: 6.8, color: "#5a7fe0" }
+ ];
+ planets.forEach(function (p, i) {
+ p.angle = (i * 2.399963) % (Math.PI * 2); // spread starting positions
+ p.x = 0; p.y = 0;
+ });
+
+ // Static starfield (regenerated on resize)
+ var stars = [];
+ function makeStars() {
+ stars = [];
+ var n = Math.floor((W * H) / 3800);
+ for (var i = 0; i < n; i++) {
+ stars.push({
+ x: Math.random() * W,
+ y: Math.random() * H,
+ r: Math.random() * 1.1 + 0.2,
+ a: Math.random() * 0.6 + 0.15
+ });
+ }
+ }
+
+ function resize() {
+ DPR = window.devicePixelRatio || 1;
+ W = window.innerWidth;
+ H = window.innerHeight;
+ canvas.width = Math.round(W * DPR);
+ canvas.height = Math.round(H * DPR);
+ ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
+ cx = W / 2;
+ cy = H / 2;
+ scale = Math.min(W, H) * 0.60; // max orbit dist 0.76 of half-min stays inside
+ makeStars();
+ }
+ window.addEventListener("resize", resize);
+ resize();
+
+ var paused = false;
+ var speedFactor = 1.0;
+ var hovered = null;
+ var mouseX = -1, mouseY = -1;
+
+ pauseBtn.addEventListener("click", function () {
+ paused = !paused;
+ pauseBtn.textContent = paused ? "Resume" : "Pause";
+ });
+
+ speedSlider.addEventListener("input", function () {
+ // 0..300 -> 0x..3x, quadratic-ish feel around 1x at 100
+ speedFactor = speedSlider.value / 100;
+ speedVal.innerHTML = speedFactor.toFixed(1) + "×";
+ });
+
+ canvas.addEventListener("mousemove", function (e) {
+ var rect = canvas.getBoundingClientRect();
+ mouseX = e.clientX - rect.left;
+ mouseY = e.clientY - rect.top;
+ });
+ canvas.addEventListener("mouseleave", function () {
+ mouseX = -1; mouseY = -1;
+ });
+
+ function pickHover() {
+ hovered = null;
+ if (mouseX < 0) return;
+ var best = 1e9;
+ for (var i = 0; i < planets.length; i++) {
+ var p = planets[i];
+ var dx = mouseX - p.x, dy = mouseY - p.y;
+ var d = Math.sqrt(dx * dx + dy * dy);
+ var hitR = Math.max(p.size + 6, 12);
+ if (d < hitR && d < best) { best = d; hovered = p; }
+ }
+ canvas.style.cursor = hovered ? "pointer" : "default";
+ }
+
+ function draw(dt) {
+ // background
+ ctx.fillStyle = "#05060f";
+ ctx.fillRect(0, 0, W, H);
+
+ // stars
+ for (var i = 0; i < stars.length; i++) {
+ var s = stars[i];
+ ctx.globalAlpha = s.a;
+ ctx.fillStyle = "#ffffff";
+ ctx.beginPath();
+ ctx.arc(s.x, s.y, s.r, 0, 6.2832);
+ ctx.fill();
+ }
+ ctx.globalAlpha = 1;
+
+ // orbit rings
+ ctx.lineWidth = 1;
+ for (i = 0; i < planets.length; i++) {
+ var p = planets[i];
+ ctx.strokeStyle = (p === hovered) ? "rgba(150,180,255,0.55)" : "rgba(120,140,200,0.18)";
+ ctx.beginPath();
+ ctx.arc(cx, cy, p.dist * scale, 0, 6.2832);
+ ctx.stroke();
+ }
+
+ // sun with glow
+ var sunR = Math.max(14, scale * 0.045);
+ var g = ctx.createRadialGradient(cx, cy, 0, cx, cy, sunR * 3);
+ g.addColorStop(0, "rgba(255,220,130,0.9)");
+ g.addColorStop(0.25, "rgba(255,180,70,0.35)");
+ g.addColorStop(1, "rgba(255,150,40,0)");
+ ctx.fillStyle = g;
+ ctx.beginPath();
+ ctx.arc(cx, cy, sunR * 3, 0, 6.2832);
+ ctx.fill();
+ var sg = ctx.createRadialGradient(cx - sunR * 0.3, cy - sunR * 0.3, sunR * 0.1, cx, cy, sunR);
+ sg.addColorStop(0, "#fff6d8");
+ sg.addColorStop(0.6, "#ffd257");
+ sg.addColorStop(1, "#f39b1d");
+ ctx.fillStyle = sg;
+ ctx.beginPath();
+ ctx.arc(cx, cy, sunR, 0, 6.2832);
+ ctx.fill();
+
+ // planets
+ for (i = 0; i < planets.length; i++) {
+ p = planets[i];
+ if (!paused) {
+ // Earth completes one orbit in 12s at 1x speed; others proportional to real periods
+ p.angle += (dt * speedFactor * (2 * Math.PI / 12)) / p.period;
+ }
+ var r = p.dist * scale;
+ p.x = cx + Math.cos(p.angle) * r;
+ p.y = cy + Math.sin(p.angle) * r;
+
+ // saturn ring
+ if (p.ring) {
+ ctx.save();
+ ctx.translate(p.x, p.y);
+ ctx.rotate(-0.45);
+ ctx.strokeStyle = "rgba(220,200,150,0.75)";
+ ctx.lineWidth = 2.4;
+ ctx.beginPath();
+ ctx.ellipse(0, 0, p.size * 1.9, p.size * 0.72, 0, 0, 6.2832);
+ ctx.stroke();
+ ctx.restore();
+ }
+
+ var pg = ctx.createRadialGradient(p.x - p.size * 0.35, p.y - p.size * 0.35, p.size * 0.15, p.x, p.y, p.size);
+ pg.addColorStop(0, "#ffffff");
+ pg.addColorStop(0.25, p.color);
+ pg.addColorStop(1, shade(p.color, -55));
+ ctx.fillStyle = pg;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, p.size, 0, 6.2832);
+ ctx.fill();
+
+ if (p === hovered) {
+ ctx.strokeStyle = "rgba(255,255,255,0.85)";
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, p.size + 4, 0, 6.2832);
+ ctx.stroke();
+ }
+ }
+ }
+
+ function shade(hex, amt) {
+ var n = parseInt(hex.slice(1), 16);
+ var r = Math.max(0, Math.min(255, (n >> 16) + amt));
+ var gr = Math.max(0, Math.min(255, ((n >> 8) & 255) + amt));
+ var b = Math.max(0, Math.min(255, (n & 255) + amt));
+ return "rgb(" + r + "," + gr + "," + b + ")";
+ }
+
+ function updateTooltip() {
+ if (hovered) {
+ tooltip.textContent = hovered.name;
+ tooltip.style.left = hovered.x + "px";
+ tooltip.style.top = hovered.y + "px";
+ tooltip.style.opacity = "1";
+ } else {
+ tooltip.style.opacity = "0";
+ }
+ }
+
+ var last = performance.now();
+ function frame(now) {
+ var dt = Math.min((now - last) / 1000, 0.1);
+ last = now;
+ pickHover();
+ draw(dt);
+ updateTooltip();
+ requestAnimationFrame(frame);
+ }
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2ca024f41f3c/claude-code.png b/data/artifacts/2ca024f41f3c/claude-code.png
new file mode 100644
index 0000000..26e0346
Binary files /dev/null and b/data/artifacts/2ca024f41f3c/claude-code.png differ
diff --git a/data/artifacts/2ca024f41f3c/gemma3-12b.html b/data/artifacts/2ca024f41f3c/gemma3-12b.html
new file mode 100644
index 0000000..d6939e0
--- /dev/null
+++ b/data/artifacts/2ca024f41f3c/gemma3-12b.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Solar System Orrery</title>
+<style>
+body { margin: 0; overflow: hidden; background-color: black; }
+#controls { position: absolute; top: 10px; left: 10px; color: white; font-family: sans-serif; }
+canvas { display: block; }
+</style>
+</head>
+<body>
+
+<div id="controls">
+ Speed: <input type="range" id="speedSlider" min="0.1" max="5" value="1" step="0.1"><br>
+ <button id="pauseButton">Pause</button>
+</div>
+
+<canvas id="solarCanvas"></canvas>
+
+<script>
+const canvas = document.getElementById('solarCanvas');
+const ctx = canvas.getContext('2d');
+
+// Orbital data (approximate)
+const planets = [
+ { name: 'Mercury', radius: 5, orbitRadius: 10, orbitalSpeed: 3 },
+ { name: 'Venus', radius: 8, orbitRadius: 25, orbitalSpeed: 2.5 },
+ { name: 'Earth', radius: 10, orbitRadius: 40, orbitalSpeed: 2 },
+ { name: 'Mars', radius: 7, orbitRadius: 60, orbitalSpeed: 1.5 },
+ { name: 'Jupiter', radius: 18, orbitRadius: 100, orbitalSpeed: 1 },
+ { name: 'Saturn', radius: 15, orbitRadius: 140, orbitalSpeed: 0.9 },
+ { name: 'Uranus', radius: 12, orbitRadius: 180, orbitalSpeed: 0.8 },
+ { name: 'Neptune', radius: 11, orbitRadius: 220, orbitalSpeed: 0.7 }
+];
+
+let paused = false;
+let timeScale = 1; // Speed slider value
+let angle = 0;
+
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+
+function drawOrbit(planet) {
+ ctx.beginPath();
+ ctx.arc(0, 0, planet.orbitRadius, 0, 2 * Math.PI);
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
+ ctx.lineWidth = 1;
+ ctx.stroke();
+}
+
+function drawPlanet(planet) {
+ const x = planet.orbitRadius * Math.cos(planet.angle);
+ const y = planet.orbitRadius * Math.sin(planet.angle);
+
+ ctx.beginPath();
+ ctx.arc(x, y, planet.radius, 0, 2 * Math.PI);
+ ctx.fillStyle = 'white';
+ ctx.fill();
+}
+
+
+function update() {
+ if (paused) return;
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = 0; i < planets.length; i++) {
+ const planet = planets[i];
+
+ planet.angle += planet.orbitalSpeed * timeScale * 0.01; // Adjust speed based on timescale
+
+ drawOrbit(planet);
+ drawPlanet(planet);
+ }
+}
+
+
+function draw() {
+ requestAnimationFrame(draw);
+ update();
+}
+
+
+canvas.addEventListener('mouseover', (event) => {
+ const x = event.offsetX;
+ const y = event.offsetY;
+
+ for (let i = 0; i < planets.length; i++) {
+ const planet = planets[i];
+ const distance = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+ const angleToPlanet = Math.atan2(y, x);
+
+ if (distance <= planet.radius && Math.abs(angleToPlanet - planet.angle) < 0.1) {
+ alert(`Planet: ${planet.name}`);
+ return; // Only show one alert at a time. Prevents multiple alerts on hover.
+ }
+ }
+});
+
+document.getElementById('speedSlider').addEventListener('input', (event) => {
+ timeScale = parseFloat(event.target.value);
+});
+
+
+document.getElementById('pauseButton').addEventListener('click', () => {
+ paused = !paused;
+ if (paused) {
+ document.getElementById('pauseButton').textContent = 'Resume';
+ } else {
+ document.getElementById('pauseButton').textContent = 'Pause';
+ }
+});
+
+draw();
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2ca024f41f3c/gemma3-12b.png b/data/artifacts/2ca024f41f3c/gemma3-12b.png
new file mode 100644
index 0000000..be9ea92
Binary files /dev/null and b/data/artifacts/2ca024f41f3c/gemma3-12b.png differ
diff --git a/data/artifacts/2ca024f41f3c/gpt.html b/data/artifacts/2ca024f41f3c/gpt.html
new file mode 100644
index 0000000..8dc733c
--- /dev/null
+++ b/data/artifacts/2ca024f41f3c/gpt.html
@@ -0,0 +1,324 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>Canvas Solar System Orrery</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ background: radial-gradient(circle at top, #050814 0%, #02030a 40%, #000 100%);
+ color: #eee;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ height: 100%;
+ overflow: hidden;
+ }
+ #container {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ }
+ #controls {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding: 0.75rem 1rem;
+ background: rgba(0,0,0,0.75);
+ border-bottom: 1px solid #222;
+ box-sizing: border-box;
+ z-index: 10;
+ }
+ #controls label {
+ font-size: 0.9rem;
+ color: #ccc;
+ white-space: nowrap;
+ }
+ #controls input[type="range"] {
+ flex: 1;
+ }
+ #controls button {
+ background: #222;
+ border: 1px solid #555;
+ border-radius: 4px;
+ padding: 0.35rem 0.75rem;
+ color: #eee;
+ cursor: pointer;
+ font-size: 0.85rem;
+ transition: background 0.15s, border-color 0.15s;
+ }
+ #controls button:hover {
+ background: #333;
+ border-color: #777;
+ }
+ #controls span.value {
+ min-width: 3rem;
+ text-align: right;
+ font-variant-numeric: tabular-nums;
+ color: #aaa;
+ font-size: 0.8rem;
+ }
+ #canvas-container {
+ flex: 1;
+ position: relative;
+ }
+ canvas {
+ display: block;
+ width: 100%;
+ height: 100%;
+ background: transparent;
+ }
+ #tooltip {
+ position: absolute;
+ pointer-events: none;
+ padding: 2px 6px;
+ font-size: 0.8rem;
+ border-radius: 3px;
+ background: rgba(0,0,0,0.8);
+ color: #fff;
+ border: 1px solid rgba(255,255,255,0.2);
+ white-space: nowrap;
+ transform: translate(8px, -20px);
+ opacity: 0;
+ transition: opacity 0.08s;
+ }
+</style>
+</head>
+<body>
+<div id="container">
+ <div id="controls">
+ <label for="speed">Speed</label>
+ <input id="speed" type="range" min="0" max="3" step="0.01" value="1">
+ <span class="value" id="speedValue">1.00×</span>
+ <button id="pauseBtn">Pause</button>
+ </div>
+ <div id="canvas-container">
+ <canvas id="orrery"></canvas>
+ <div id="tooltip"></div>
+ </div>
+</div>
+
+<script>
+(function() {
+ const canvas = document.getElementById('orrery');
+ const ctx = canvas.getContext('2d');
+ const tooltip = document.getElementById('tooltip');
+ const speedSlider = document.getElementById('speed');
+ const speedValueLabel = document.getElementById('speedValue');
+ const pauseBtn = document.getElementById('pauseBtn');
+
+ let width = 0, height = 0, cx = 0, cy = 0;
+ let running = true;
+ let baseTime = performance.now();
+ let lastFrameTime = baseTime;
+ let simTime = 0;
+
+ const AU = 50; // visual scale unit for Earth's orbit radius
+
+ // Orbital periods in Earth days (approx); used proportionally
+ const EARTH_PERIOD = 365.25;
+ const planets = [
+ { name: 'Mercury', color: '#c9c9c9', radius: 3, orbitRadius: 0.39 * AU, periodDays: 88 },
+ { name: 'Venus', color: '#f5d28a', radius: 4, orbitRadius: 0.72 * AU, periodDays: 225 },
+ { name: 'Earth', color: '#4fa9ff', radius: 4, orbitRadius: 1.0 * AU, periodDays: EARTH_PERIOD },
+ { name: 'Mars', color: '#ff6f5e', radius: 3.5, orbitRadius: 1.52 * AU, periodDays: 687 },
+ { name: 'Jupiter', color: '#f0e0c0', radius: 7, orbitRadius: 5.2 * AU, periodDays: 4331 },
+ { name: 'Saturn', color: '#f6f1d6', radius: 6, orbitRadius: 9.5 * AU, periodDays: 10747 },
+ { name: 'Uranus', color: '#9be3ff', radius: 5, orbitRadius: 19.2 * AU, periodDays: 30589 },
+ { name: 'Neptune', color: '#497cff', radius: 5, orbitRadius: 30.1 * AU, periodDays: 59800 }
+ ];
+
+ // Precompute angular velocities relative to Earth
+ planets.forEach(p => {
+ const rel = EARTH_PERIOD / p.periodDays; // Mercury fastest > 1, Neptune < 1
+ p.angularVel = rel * 0.4; // base radians per simulated year; scaled visually
+ p.angle = Math.random() * Math.PI * 2;
+ });
+
+ function resize() {
+ const rect = canvas.parentElement.getBoundingClientRect();
+ const dpr = window.devicePixelRatio || 1;
+ width = rect.width;
+ height = rect.height;
+ canvas.width = width * dpr;
+ canvas.height = height * dpr;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ cx = width / 2;
+ cy = height / 2;
+
+ // Auto-scale AU to fit within viewport while preserving relative distances
+ const maxOrbit = planets[planets.length - 1].orbitRadius; // Neptune
+ const margin = 30;
+ const maxRadius = Math.min(width, height) / 2 - margin;
+ const scale = maxRadius / maxOrbit;
+ planets.forEach(p => p.visualOrbit = p.orbitRadius * scale);
+ }
+
+ window.addEventListener('resize', resize);
+ resize();
+
+ function drawBackground() {
+ ctx.save();
+ ctx.fillStyle = '#000000';
+ ctx.fillRect(0, 0, width, height);
+
+ // Sparse stars
+ ctx.fillStyle = 'rgba(255,255,255,0.5)';
+ const starCount = Math.floor((width * height) / 15000);
+ for (let i = 0; i < starCount; i++) {
+ const x = Math.random() * width;
+ const y = Math.random() * height;
+ const r = Math.random() * 0.6 + 0.2;
+ ctx.beginPath();
+ ctx.arc(x, y, r, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ ctx.restore();
+ }
+
+ // Draw static background once (stars)
+ drawBackground();
+
+ function render() {
+ // Clear dynamic layer (but keep the starry background by redrawing quickly)
+ ctx.clearRect(0, 0, width, height);
+ drawBackground();
+
+ // Draw sun
+ const sunRadius = 12;
+ const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, sunRadius * 3);
+ gradient.addColorStop(0, '#fff8c2');
+ gradient.addColorStop(0.3, '#ffd866');
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = gradient;
+ ctx.beginPath();
+ ctx.arc(cx, cy, sunRadius * 3, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.fillStyle = '#ffeb7f';
+ ctx.beginPath();
+ ctx.arc(cx, cy, sunRadius, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Orbits
+ ctx.save();
+ ctx.strokeStyle = 'rgba(180,180,200,0.25)';
+ ctx.lineWidth = 1;
+ ctx.setLineDash([4, 4]);
+ planets.forEach(p => {
+ ctx.beginPath();
+ ctx.arc(cx, cy, p.visualOrbit, 0, Math.PI * 2);
+ ctx.stroke();
+ });
+ ctx.restore();
+
+ const hovered = getHoveredPlanet(lastMouse.x, lastMouse.y);
+ // Planets
+ planets.forEach(p => {
+ const x = cx + Math.cos(p.angle) * p.visualOrbit;
+ const y = cy + Math.sin(p.angle) * p.visualOrbit;
+ p.screenX = x;
+ p.screenY = y;
+
+ ctx.beginPath();
+ ctx.fillStyle = p === hovered ? '#ffffff' : p.color;
+ ctx.arc(x, y, p.radius, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Simple glow for outer planets for visibility
+ if (p === hovered) {
+ ctx.save();
+ ctx.strokeStyle = 'rgba(255,255,255,0.6)';
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ ctx.arc(x, y, p.radius + 3, 0, Math.PI * 2);
+ ctx.stroke();
+ ctx.restore();
+ }
+ });
+ }
+
+ function update(dt) {
+ const speed = parseFloat(speedSlider.value);
+ simTime += dt * speed;
+ planets.forEach(p => {
+ p.angle += p.angularVel * dt * speed;
+ p.angle %= Math.PI * 2;
+ });
+ }
+
+ function loop(now) {
+ const dt = (now - lastFrameTime) / 1000; // seconds
+ lastFrameTime = now;
+
+ if (running) {
+ update(dt);
+ }
+ render();
+ requestAnimationFrame(loop);
+ }
+ requestAnimationFrame(loop);
+
+ // Interactivity
+ speedSlider.addEventListener('input', () => {
+ const speed = parseFloat(speedSlider.value);
+ speedValueLabel.textContent = speed.toFixed(2) + '×';
+ });
+
+ pauseBtn.addEventListener('click', () => {
+ running = !running;
+ pauseBtn.textContent = running ? 'Pause' : 'Resume';
+ });
+
+ const lastMouse = { x: -1000, y: -1000, inside: false };
+
+ function getHoveredPlanet(x, y) {
+ if (!lastMouse.inside) return null;
+ const hitRadius = 8;
+ let closest = null;
+ let closestDist = Infinity;
+ planets.forEach(p => {
+ const dx = x - p.screenX;
+ const dy = y - p.screenY;
+ const d = Math.sqrt(dx*dx + dy*dy);
+ if (d < p.radius + hitRadius && d < closestDist) {
+ closest = p;
+ closestDist = d;
+ }
+ });
+ return closest;
+ }
+
+ function updateTooltip(x, y) {
+ const planet = getHoveredPlanet(x, y);
+ if (planet) {
+ tooltip.textContent = planet.name;
+ const rect = canvas.getBoundingClientRect();
+ const tx = x - rect.left;
+ const ty = y - rect.top;
+ tooltip.style.left = tx + 'px';
+ tooltip.style.top = ty + 'px';
+ tooltip.style.opacity = '1';
+ } else {
+ tooltip.style.opacity = '0';
+ }
+ }
+
+ canvas.addEventListener('mousemove', (e) => {
+ const rect = canvas.getBoundingClientRect();
+ lastMouse.x = e.clientX - rect.left;
+ lastMouse.y = e.clientY - rect.top;
+ lastMouse.inside = true;
+ updateTooltip(lastMouse.x, lastMouse.y);
+ });
+
+ canvas.addEventListener('mouseleave', () => {
+ lastMouse.inside = false;
+ tooltip.style.opacity = '0';
+ });
+
+ // Ensure initial label
+ speedValueLabel.textContent = parseFloat(speedSlider.value).toFixed(2) + '×';
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2ca024f41f3c/gpt.png b/data/artifacts/2ca024f41f3c/gpt.png
new file mode 100644
index 0000000..745a9f2
Binary files /dev/null and b/data/artifacts/2ca024f41f3c/gpt.png differ
diff --git a/data/artifacts/2ca024f41f3c/grok.html b/data/artifacts/2ca024f41f3c/grok.html
new file mode 100644
index 0000000..85c704e
--- /dev/null
+++ b/data/artifacts/2ca024f41f3c/grok.html
@@ -0,0 +1,355 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Solar System Orrery</title>
+<style>
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+ body {
+ background: #050510;
+ color: #e0e0ff;
+ font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
+ overflow: hidden;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ }
+ canvas {
+ display: block;
+ cursor: crosshair;
+ }
+ #ui {
+ position: fixed;
+ bottom: 24px;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ background: rgba(10, 12, 30, 0.85);
+ border: 1px solid rgba(120, 140, 255, 0.25);
+ border-radius: 12px;
+ padding: 12px 20px;
+ backdrop-filter: blur(8px);
+ box-shadow: 0 8px 32px rgba(0,0,0,0.5);
+ z-index: 10;
+ }
+ #ui label {
+ font-size: 13px;
+ opacity: 0.85;
+ white-space: nowrap;
+ }
+ #speed {
+ width: 140px;
+ accent-color: #6af;
+ }
+ #pause {
+ background: linear-gradient(135deg, #3a4a8a, #2a3a6a);
+ color: #eef;
+ border: 1px solid rgba(140,160,255,0.4);
+ border-radius: 8px;
+ padding: 8px 16px;
+ font-size: 13px;
+ cursor: pointer;
+ min-width: 72px;
+ }
+ #pause:hover { background: linear-gradient(135deg, #4a5aaa, #3a4a8a); }
+ #tooltip {
+ position: fixed;
+ pointer-events: none;
+ background: rgba(15, 18, 40, 0.92);
+ border: 1px solid rgba(140, 160, 255, 0.45);
+ color: #fff;
+ padding: 6px 12px;
+ border-radius: 6px;
+ font-size: 13px;
+ letter-spacing: 0.03em;
+ opacity: 0;
+ transition: opacity 0.12s;
+ z-index: 20;
+ white-space: nowrap;
+ }
+ #title {
+ position: fixed;
+ top: 18px;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 14px;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ opacity: 0.5;
+ font-weight: 300;
+ }
+</style>
+</head>
+<body>
+<div id="title">Solar System Orrery</div>
+<canvas id="c"></canvas>
+<div id="tooltip"></div>
+<div id="ui">
+ <label for="speed">Speed</label>
+ <input type="range" id="speed" min="0" max="5" step="0.05" value="1">
+ <button id="pause">Pause</button>
+</div>
+<script>
+(function () {
+ const canvas = document.getElementById('c');
+ const ctx = canvas.getContext('2d');
+ const tooltip = document.getElementById('tooltip');
+ const speedSlider = document.getElementById('speed');
+ const pauseBtn = document.getElementById('pause');
+
+ let W, H, cx, cy;
+ let dpr = 1;
+ let paused = false;
+ let speedMul = 1;
+ let t = 0;
+ let last = performance.now();
+ let mouse = { x: -9999, y: -9999 };
+ let stars = [];
+
+ // Orbital periods in Earth years (approx). Distances scaled for visual layout.
+ // radius = visual orbit radius in "AU units", size = planet draw radius
+ const planets = [
+ { name: 'Mercury', color: '#b0b0b0', radius: 0.39, size: 3.2, period: 0.241, angle: 0.8 },
+ { name: 'Venus', color: '#e8cda0', radius: 0.72, size: 5.5, period: 0.615, angle: 2.1 },
+ { name: 'Earth', color: '#6ab0ff', radius: 1.00, size: 5.8, period: 1.000, angle: 0.4 },
+ { name: 'Mars', color: '#e07050', radius: 1.52, size: 4.2, period: 1.881, angle: 3.5 },
+ { name: 'Jupiter', color: '#d4a86a', radius: 2.80, size: 12, period: 11.86, angle: 1.2 },
+ { name: 'Saturn', color: '#e8d5a0', radius: 3.60, size: 10, period: 29.46, angle: 4.0, ring: true },
+ { name: 'Uranus', color: '#7ec8e0', radius: 4.40, size: 7.5, period: 84.01, angle: 0.6 },
+ { name: 'Neptune', color: '#4a6aff', radius: 5.10, size: 7.2, period: 164.8, angle: 2.8 }
+ ];
+
+ const SUN_SIZE = 18;
+
+ function resize() {
+ dpr = Math.min(window.devicePixelRatio || 1, 2);
+ W = window.innerWidth;
+ H = window.innerHeight;
+ canvas.width = W * dpr;
+ canvas.height = 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 - 10;
+ buildStars();
+ }
+
+ function buildStars() {
+ stars = [];
+ const n = Math.floor((W * H) / 3500);
+ for (let i = 0; i < n; i++) {
+ stars.push({
+ x: Math.random() * W,
+ y: Math.random() * H,
+ r: Math.random() * 1.2 + 0.2,
+ a: Math.random() * 0.6 + 0.2
+ });
+ }
+ }
+
+ function scale() {
+ // Fit outermost orbit with padding
+ const maxR = planets[planets.length - 1].radius;
+ const pad = Math.min(W, H) * 0.08;
+ return (Math.min(W, H) / 2 - pad - 20) / maxR;
+ }
+
+ function planetPos(p, s) {
+ const ang = p.angle + (t * Math.PI * 2) / p.period;
+ const r = p.radius * s;
+ return {
+ x: cx + Math.cos(ang) * r,
+ y: cy + Math.sin(ang) * r,
+ ang
+ };
+ }
+
+ function drawStars() {
+ for (const st of stars) {
+ ctx.beginPath();
+ ctx.fillStyle = `rgba(220,225,255,${st.a})`;
+ ctx.arc(st.x, st.y, st.r, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ function drawOrbit(p, s) {
+ ctx.beginPath();
+ ctx.strokeStyle = 'rgba(120, 140, 200, 0.18)';
+ ctx.lineWidth = 1;
+ ctx.arc(cx, cy, p.radius * s, 0, Math.PI * 2);
+ ctx.stroke();
+ }
+
+ function drawSun() {
+ const g = ctx.createRadialGradient(cx, cy, 0, cx, cy, SUN_SIZE * 2.5);
+ g.addColorStop(0, '#fff8e0');
+ g.addColorStop(0.25, '#ffe060');
+ g.addColorStop(0.5, '#ff9020');
+ g.addColorStop(1, 'rgba(255,80,0,0)');
+ ctx.beginPath();
+ ctx.fillStyle = g;
+ ctx.arc(cx, cy, SUN_SIZE * 2.5, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.fillStyle = '#ffcc33';
+ ctx.arc(cx, cy, SUN_SIZE, 0, Math.PI * 2);
+ ctx.fill();
+
+ // core highlight
+ ctx.beginPath();
+ ctx.fillStyle = 'rgba(255,255,220,0.55)';
+ ctx.arc(cx - 4, cy - 4, SUN_SIZE * 0.35, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ function drawPlanet(p, pos) {
+ // subtle glow
+ const glow = ctx.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, p.size * 2.2);
+ glow.addColorStop(0, p.color);
+ glow.addColorStop(0.4, p.color + '88');
+ glow.addColorStop(1, 'transparent');
+ ctx.beginPath();
+ ctx.fillStyle = glow;
+ ctx.arc(pos.x, pos.y, p.size * 2.2, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Saturn ring behind
+ if (p.ring) {
+ ctx.save();
+ ctx.translate(pos.x, pos.y);
+ ctx.rotate(0.4);
+ ctx.scale(1, 0.35);
+ ctx.beginPath();
+ ctx.strokeStyle = 'rgba(210, 190, 140, 0.55)';
+ ctx.lineWidth = 2.5;
+ ctx.arc(0, 0, p.size * 1.9, 0, Math.PI * 2);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.strokeStyle = 'rgba(180, 160, 110, 0.35)';
+ ctx.lineWidth = 1.5;
+ ctx.arc(0, 0, p.size * 2.3, 0, Math.PI * 2);
+ ctx.stroke();
+ ctx.restore();
+ }
+
+ ctx.beginPath();
+ ctx.fillStyle = p.color;
+ ctx.arc(pos.x, pos.y, p.size, 0, Math.PI * 2);
+ ctx.fill();
+
+ // specular
+ ctx.beginPath();
+ ctx.fillStyle = 'rgba(255,255,255,0.35)';
+ ctx.arc(pos.x - p.size * 0.3, pos.y - p.size * 0.3, p.size * 0.35, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Saturn ring in front (lower half)
+ if (p.ring) {
+ ctx.save();
+ ctx.translate(pos.x, pos.y);
+ ctx.rotate(0.4);
+ ctx.scale(1, 0.35);
+ ctx.beginPath();
+ ctx.strokeStyle = 'rgba(220, 200, 150, 0.5)';
+ ctx.lineWidth = 2.5;
+ ctx.arc(0, 0, p.size * 1.9, 0.05, Math.PI - 0.05);
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+
+ function hitTest(s) {
+ // Check sun
+ const ds = Math.hypot(mouse.x - cx, mouse.y - cy);
+ if (ds < SUN_SIZE + 4) return { name: 'Sun', x: cx, y: cy };
+
+ for (let i = planets.length - 1; i >= 0; i--) {
+ const p = planets[i];
+ const pos = planetPos(p, s);
+ const d = Math.hypot(mouse.x - pos.x, mouse.y - pos.y);
+ const hitR = Math.max(p.size + 6, 10);
+ if (d < hitR) return { name: p.name, x: pos.x, y: pos.y };
+ }
+ return null;
+ }
+
+ function frame(now) {
+ const dt = Math.min((now - last) / 1000, 0.1);
+ last = now;
+ if (!paused) t += dt * speedMul;
+
+ const s = scale();
+
+ ctx.clearRect(0, 0, W, H);
+
+ // deep space vignette
+ const vg = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(W, H) * 0.7);
+ vg.addColorStop(0, '#0a0a22');
+ vg.addColorStop(1, '#030308');
+ ctx.fillStyle = vg;
+ ctx.fillRect(0, 0, W, H);
+
+ drawStars();
+
+ for (const p of planets) drawOrbit(p, s);
+ drawSun();
+
+ const positions = [];
+ for (const p of planets) {
+ const pos = planetPos(p, s);
+ positions.push(pos);
+ drawPlanet(p, pos);
+ }
+
+ const hit = hitTest(s);
+ if (hit) {
+ tooltip.textContent = hit.name;
+ tooltip.style.opacity = '1';
+ let tx = hit.x + 14;
+ let ty = hit.y - 28;
+ if (tx + 80 > W) tx = hit.x - 80;
+ if (ty < 8) ty = hit.y + 18;
+ tooltip.style.left = tx + 'px';
+ tooltip.style.top = ty + 'px';
+ canvas.style.cursor = 'pointer';
+ } else {
+ tooltip.style.opacity = '0';
+ canvas.style.cursor = 'crosshair';
+ }
+
+ requestAnimationFrame(frame);
+ }
+
+ speedSlider.addEventListener('input', () => {
+ speedMul = parseFloat(speedSlider.value);
+ });
+
+ pauseBtn.addEventListener('click', () => {
+ paused = !paused;
+ pauseBtn.textContent = paused ? 'Resume' : 'Pause';
+ });
+
+ canvas.addEventListener('mousemove', (e) => {
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+ });
+ canvas.addEventListener('mouseleave', () => {
+ mouse.x = -9999;
+ mouse.y = -9999;
+ });
+
+ window.addEventListener('resize', resize);
+ resize();
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2ca024f41f3c/grok.png b/data/artifacts/2ca024f41f3c/grok.png
new file mode 100644
index 0000000..77b2cb8
Binary files /dev/null and b/data/artifacts/2ca024f41f3c/grok.png differ
diff --git a/data/artifacts/2ca024f41f3c/hermes3-8b.html b/data/artifacts/2ca024f41f3c/hermes3-8b.html
new file mode 100644
index 0000000..2b26ce2
--- /dev/null
+++ b/data/artifacts/2ca024f41f3c/hermes3-8b.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>Solar System Orrery</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ }
+
+ canvas {
+ display: block;
+ }
+
+ #controls {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ background-color: rgba(255, 255, 255, 0.8);
+ padding: 5px;
+ border-radius: 5px;
+ }
+
+ label {
+ display: block;
+ margin-bottom: 5px;
+ }
+
+ input[type="range"] {
+ width: 100%;
+ }
+ </style>
+</head>
+<body>
+ <div id="controls">
+ <label for="speed">Speed:</label>
+ <input type="range" id="speed" min="1" max="500" value="50">
+
+ <label for="pause">Pause:</label>
+ <input type="checkbox" id="pause">
+ </div>
+
+ <canvas id="sky"></canvas>
+ <canvas id="orbits"></canvas>
+ <canvas id="planets"></canvas>
+
+ <script>
+ const sky = document.getElementById('sky');
+ const orbits = document.getElementById('orbits');
+ const planets = document.getElementById('planets');
+
+ const ctxSky = sky.getContext('2d');
+ const ctxOrbits = orbits.getContext('2d');
+ const ctxPlanets = planets.getContext('2d');
+
+ const sunRadius = 50;
+ let speed = 50;
+
+ function drawSun(x, y) {
+ ctxSky.beginPath();
+ ctxSky.arc(x, y, sunRadius, 0, Math.PI * 2);
+ ctxSky.fillStyle = 'yellow';
+ ctxSky.fill();
+ ctxSky.closePath();
+ }
+
+ drawSun(sky.width / 2, sky.height / 2);
+
+ const planetsData = [
+ { name: 'Mercury', radius: 12, orbitRadius: 75, period: 100 },
+ { name: 'Venus', radius: 14, orbitRadius: 115, period: 150 },
+ { name: 'Earth', radius: 16, orbitRadius: 165, period: 200 },
+ { name: 'Mars', radius: 18, orbitRadius: 215, period: 250 },
+ { name: 'Jupiter', radius: 22, orbitRadius: 275, period: 350 },
+ { name: 'Saturn', radius: 20, orbitRadius: 325, period: 450 },
+ { name: 'Uranus', radius: 18, orbitRadius: 375, period: 550 },
+ { name: 'Neptune', radius: 16, orbitRadius: 425, period: 600 }
+ ];
+
+ planetsData.forEach(({name, radius, orbitRadius, period}, index) => {
+ const planet = {
+ x: Math.cos(index * (Math.PI / 4) + speed / 50) * orbitRadius,
+ y: Math.sin(index * (Math.PI / 4)) * orbitRadius
+ };
+
+ drawPlanet(planet.x, planet.y, radius);
+ drawOrbit(orbitRadius);
+
+ function drawPlanet(x, y, size) {
+ ctxPlanets.beginPath();
+ ctxPlanets.arc(x, y, size, 0, Math.PI * 2);
+ ctxPlanets.fillStyle = 'white';
+ ctxPlanets.fill();
+
+ const textWidth = ctxPlanets.measureText(name).width;
+ ctxPlanets.strokeStyle = 'black';
+ ctxPlanets.font = "14px Arial";
+ ctxPlanets.textAlign = "center";
+ ctxPlanets.fillText(name, x - textWidth / 2, y + size * 0.8);
+ ctxPlanets.stroke();
+ }
+
+ function drawOrbit(radius) {
+ ctxOrbits.beginPath();
+ ctxOrbits.arc(sky.width / 2, sky.height / 2, radius, 0, Math.PI * 2);
+ ctxOrbits.strokeStyle = 'rgba(255, 255, 255, 0.5)';
+ ctxOrbits.lineWidth = 3;
+ ctxOrbits.stroke();
+ }
+ });
+
+ function update() {
+ planetsData.forEach(({name, radius, orbitRadius, period}, index) => {
+ const planet = {
+ x: Math.cos(index * (Math.PI / 4) + speed / 50) * orbitRadius,
+ y: Math.sin(index * (Math.PI / 4)) * orbitRadius
+ };
+
+ drawPlanet(planet.x, planet.y, radius);
+
+ function drawPlanet(x, y, size) {
+ ctxPlanets.beginPath();
+ ctxPlanets.arc(x, y, size, 0, Math.PI * 2);
+ ctxPlanets.fillStyle = 'white';
+ ctxPlanets.fill();
+
+ const textWidth = ctxPlanets.measureText(name).width;
+ ctxPlanets.strokeStyle = 'black';
+ ctxPlanets.font = "14px Arial";
+ ctxPlanets.textAlign = "center";
+ ctxPlanets.fillText(name, x - textWidth / 2, y + size * 0.8);
+ ctxPlanets.stroke();
+ }
+ });
+
+ requestAnimationFrame(update);
+ }
+
+ document.getElementById('speed').oninput = e => {
+ speed = parseInt(e.target.value);
+ };
+
+ document.getElementById('pause').onchange = e => {
+ if (e.target.checked) {
+ update = () => {};
+ } else {
+ update();
+ }
+ };
+
+ update();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2ca024f41f3c/hermes3-8b.png b/data/artifacts/2ca024f41f3c/hermes3-8b.png
new file mode 100644
index 0000000..33eb53d
Binary files /dev/null and b/data/artifacts/2ca024f41f3c/hermes3-8b.png differ
diff --git a/data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.html b/data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..8ff9f84
--- /dev/null
+++ b/data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Solar System Orrery</title>
+ <style>
+ body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #000; }
+ canvas { border: 1px solid #fff; }
+ #controls { position: absolute; top: 10px; left: 10px; color: white; font-family: Arial, sans-serif; }
+ input[type="range"] { width: 200px; margin-right: 10px; }
+ </style>
+</head>
+<body>
+ <div id="controls">
+ Speed: <input type="range" id="speedSlider" min="0.1" max="5" step="0.1" value="1">
+ <button id="pauseButton">Pause</button>
+ </div>
+ <canvas id="solarSystem"></canvas>
+ <script>
+ const canvas = document.getElementById('solarSystem');
+ const ctx = canvas.getContext('2d');
+ canvas.width = 800;
+ canvas.height = 600;
+
+ const planets = [
+ { name: 'Mercury', distance: 100, radius: 3, color: '#B57EDC', speed: 4.79 },
+ { name: 'Venus', distance: 140, radius: 5, color: '#FFA07A', speed: 3.5 },
+ { name: 'Earth', distance: 180, radius: 6, color: '#00BFFF', speed: 2.98 },
+ { name: 'Mars', distance: 220, radius: 4, color: '#FF7F50', speed: 2.41 },
+ { name: 'Jupiter', distance: 300, radius: 12, color: '#DAA520', speed: 1.31 },
+ { name: 'Saturn', distance: 380, radius: 10, color: '#FFD700', speed: 0.96 },
+ { name: 'Uranus', distance: 450, radius: 8, color: '#4682B4', speed: 0.68 },
+ { name: 'Neptune', distance: 530, radius: 7, color: '#1E90FF', speed: 0.54 }
+ ];
+
+ let angle = 0;
+ let paused = false;
+
+ function drawOrbit(radius) {
+ ctx.beginPath();
+ ctx.arc(canvas.width / 2, canvas.height / 2, radius, 0, Math.PI * 2);
+ ctx.strokeStyle = '#888';
+ ctx.stroke();
+ }
+
+ function drawPlanets() {
+ planets.forEach(planet => {
+ const x = canvas.width / 2 + planet.distance * Math.cos(angle * (planet.speed / 1.5));
+ const y = canvas.height / 2 + planet.distance * Math.sin(angle * (planet.speed / 1.5));
+
+ ctx.beginPath();
+ ctx.arc(x, y, planet.radius, 0, Math.PI * 2);
+ ctx.fillStyle = planet.color;
+ ctx.fill();
+
+ if (isMouseOverPlanet(x, y, planet.radius)) {
+ ctx.font = '14px Arial';
+ ctx.fillStyle = '#fff';
+ ctx.fillText(planet.name, x + planet.radius + 5, y);
+ }
+ });
+ }
+
+ function isMouseOverPlanet(px, py, pr) {
+ return Math.sqrt((mouseX - px) ** 2 + (mouseY - py) ** 2) <= pr;
+ }
+
+ let mouseX = 0;
+ let mouseY = 0;
+
+ canvas.addEventListener('mousemove', e => {
+ const rect = canvas.getBoundingClientRect();
+ mouseX = e.clientX - rect.left;
+ mouseY = e.clientY - rect.top;
+ });
+
+ function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ planets.forEach(p => drawOrbit(p.distance));
+ drawPlanets();
+
+ if (!paused) angle += parseFloat(document.getElementById('speedSlider').value) * 0.01;
+
+ requestAnimationFrame(animate);
+ }
+
+ document.getElementById('pauseButton').addEventListener('click', () => {
+ paused = !paused;
+ document.getElementById('pauseButton').textContent = paused ? 'Resume' : 'Pause';
+ });
+
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.png b/data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..0ee71d5
Binary files /dev/null and b/data/artifacts/2ca024f41f3c/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/2ca024f41f3c/kimi.html b/data/artifacts/2ca024f41f3c/kimi.html
new file mode 100644
index 0000000..dd0433a
--- /dev/null
+++ b/data/artifacts/2ca024f41f3c/kimi.html
@@ -0,0 +1,257 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Solar System Orrery</title>
+<style>
+body {
+ margin: 0;
+ background: radial-gradient(circle at center, #1a1a2e 0%, #000000 100%);
+ color: #fff;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 100vh;
+ overflow: hidden;
+}
+#canvas {
+ background: transparent;
+ box-shadow: 0 0 50px rgba(0,0,0,0.8);
+ border-radius: 4px;
+}
+#controls {
+ margin-top: 20px;
+ display: flex;
+ gap: 30px;
+ align-items: center;
+ background: rgba(255,255,255,0.05);
+ padding: 15px 30px;
+ border-radius: 30px;
+ border: 1px solid rgba(255,255,255,0.1);
+}
+button {
+ background: #2a2a4a;
+ color: white;
+ border: 1px solid #4a4a6a;
+ padding: 8px 20px;
+ border-radius: 20px;
+ cursor: pointer;
+ font-size: 14px;
+ transition: all 0.3s;
+}
+button:hover {
+ background: #3a3a6a;
+ border-color: #6a6a8a;
+}
+label {
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+input[type="range"] {
+ width: 150px;
+ cursor: pointer;
+}
+#tooltip {
+ position: absolute;
+ background: rgba(0,0,0,0.9);
+ color: #fff;
+ padding: 8px 12px;
+ border-radius: 6px;
+ font-size: 14px;
+ pointer-events: none;
+ display: none;
+ border: 1px solid rgba(255,255,255,0.2);
+ box-shadow: 0 4px 6px rgba(0,0,0,0.3);
+ z-index: 10;
+}
+</style>
+</head>
+<body>
+<canvas id="canvas" width="900" height="700"></canvas>
+<div id="controls">
+ <button id="pauseBtn">Pause</button>
+ <label>
+ Speed: <span id="speedVal">1x</span>
+ <input type="range" id="speedSlider" min="0" max="5" step="0.1" value="1">
+ </label>
+</div>
+<div id="tooltip"></div>
+<script>
+const canvas = document.getElementById('canvas');
+const ctx = canvas.getContext('2d');
+const tooltip = document.getElementById('tooltip');
+const pauseBtn = document.getElementById('pauseBtn');
+const speedSlider = document.getElementById('speedSlider');
+const speedVal = document.getElementById('speedVal');
+
+const dpr = window.devicePixelRatio || 1;
+const width = 900;
+const height = 700;
+canvas.style.width = width + 'px';
+canvas.style.height = height + 'px';
+canvas.width = width * dpr;
+canvas.height = height * dpr;
+ctx.scale(dpr, dpr);
+
+const centerX = width / 2;
+const centerY = height / 2;
+
+const planets = [
+ { name: 'Mercury', dist: 60, radius: 4, period: 0.24, color: '#A5A5A5', angle: Math.random() * Math.PI * 2 },
+ { name: 'Venus', dist: 90, radius: 7, period: 0.62, color: '#E3BB76', angle: Math.random() * Math.PI * 2 },
+ { name: 'Earth', dist: 130, radius: 7, period: 1.0, color: '#4F83CC', angle: Math.random() * Math.PI * 2 },
+ { name: 'Mars', dist: 170, radius: 5, period: 1.88, color: '#C1440E', angle: Math.random() * Math.PI * 2 },
+ { name: 'Jupiter', dist: 240, radius: 18, period: 11.86, color: '#D4A574', angle: Math.random() * Math.PI * 2 },
+ { name: 'Saturn', dist: 300, radius: 15, period: 29.5, color: '#EAD6B8', angle: Math.random() * Math.PI * 2, hasRings: true },
+ { name: 'Uranus', dist: 360, radius: 10, period: 84, color: '#A6D9F0', angle: Math.random() * Math.PI * 2 },
+ { name: 'Neptune', dist: 420, radius: 10, period: 165, color: '#5B5DDF', angle: Math.random() * Math.PI * 2 }
+];
+
+let isPaused = false;
+let speedMultiplier = 1;
+let hoveredPlanet = null;
+const mouse = { x: 0, y: 0 };
+
+pauseBtn.addEventListener('click', () => {
+ isPaused = !isPaused;
+ pauseBtn.textContent = isPaused ? 'Resume' : 'Pause';
+});
+
+speedSlider.addEventListener('input', (e) => {
+ speedMultiplier = parseFloat(e.target.value);
+ speedVal.textContent = speedMultiplier.toFixed(1) + 'x';
+});
+
+canvas.addEventListener('mousemove', (e) => {
+ const rect = canvas.getBoundingClientRect();
+ mouse.x = e.clientX - rect.left;
+ mouse.y = e.clientY - rect.top;
+});
+
+canvas.addEventListener('mouseleave', () => {
+ mouse.x = -1000;
+ mouse.y = -1000;
+ tooltip.style.display = 'none';
+});
+
+function drawSun() {
+ ctx.save();
+ ctx.shadowBlur = 30;
+ ctx.shadowColor = '#FDB813';
+ ctx.beginPath();
+ ctx.arc(centerX, centerY, 25, 0, Math.PI * 2);
+ ctx.fillStyle = '#FDB813';
+ ctx.fill();
+
+ ctx.shadowBlur = 60;
+ ctx.shadowColor = 'rgba(253, 184, 19, 0.5)';
+ ctx.beginPath();
+ ctx.arc(centerX, centerY, 20, 0, Math.PI * 2);
+ ctx.fillStyle = '#FFD700';
+ ctx.fill();
+ ctx.restore();
+}
+
+function drawOrbit(dist) {
+ ctx.beginPath();
+ ctx.arc(centerX, centerY, dist, 0, Math.PI * 2);
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.08)';
+ ctx.lineWidth = 1;
+ ctx.stroke();
+}
+
+function drawPlanet(planet) {
+ const x = centerX + Math.cos(planet.angle) * planet.dist;
+ const y = centerY + Math.sin(planet.angle) * planet.dist;
+
+ planet.x = x;
+ planet.y = y;
+
+ ctx.beginPath();
+ ctx.arc(x, y, planet.radius, 0, Math.PI * 2);
+ ctx.fillStyle = planet.color;
+ ctx.fill();
+
+ if (hoveredPlanet === planet) {
+ ctx.beginPath();
+ ctx.arc(x, y, planet.radius + 3, 0, Math.PI * 2);
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+ }
+
+ if (planet.hasRings) {
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.rotate(Math.PI / 6);
+ ctx.beginPath();
+ ctx.ellipse(0, 0, planet.radius * 2.2, planet.radius * 0.6, 0, 0, Math.PI * 2);
+ ctx.strokeStyle = 'rgba(200, 180, 140, 0.6)';
+ ctx.lineWidth = 3;
+ ctx.stroke();
+ ctx.restore();
+ }
+}
+
+function update() {
+ if (!isPaused) {
+ const baseSpeed = 0.005 * speedMultiplier;
+ planets.forEach(planet => {
+ planet.angle += baseSpeed / planet.period;
+ });
+ }
+}
+
+function checkHover() {
+ hoveredPlanet = null;
+ let minDist = Infinity;
+
+ for (let i = planets.length - 1; i >= 0; i--) {
+ const p = planets[i];
+ const dx = mouse.x - p.x;
+ const dy = mouse.y - p.y;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+
+ if (dist < p.radius + 5 && dist < minDist) {
+ minDist = dist;
+ hoveredPlanet = p;
+ }
+ }
+
+ if (hoveredPlanet) {
+ tooltip.textContent = hoveredPlanet.name;
+ tooltip.style.display = 'block';
+ const rect = canvas.getBoundingClientRect();
+ tooltip.style.left = (rect.left + hoveredPlanet.x + 15) + 'px';
+ tooltip.style.top = (rect.top + hoveredPlanet.y - 15) + 'px';
+ canvas.style.cursor = 'pointer';
+ } else {
+ tooltip.style.display = 'none';
+ canvas.style.cursor = 'default';
+ }
+}
+
+function render() {
+ ctx.clearRect(0, 0, width, height);
+
+ planets.forEach(p => drawOrbit(p.dist));
+ drawSun();
+ planets.forEach(drawPlanet);
+ checkHover();
+}
+
+function animate() {
+ update();
+ render();
+ requestAnimationFrame(animate);
+}
+
+animate();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2ca024f41f3c/kimi.png b/data/artifacts/2ca024f41f3c/kimi.png
new file mode 100644
index 0000000..b5fdfb3
Binary files /dev/null and b/data/artifacts/2ca024f41f3c/kimi.png differ
diff --git a/data/artifacts/2ca024f41f3c/qwen25-7b.html b/data/artifacts/2ca024f41f3c/qwen25-7b.html
new file mode 100644
index 0000000..0e0089a
--- /dev/null
+++ b/data/artifacts/2ca024f41f3c/qwen25-7b.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Solar System Orrery</title>
+ <style>
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ background-color: #000;
+ font-family: Arial, sans-serif;
+ }
+ canvas {
+ border: 2px solid white;
+ box-shadow: 0 0 20px rgba(255, 255, 255, 0.5);
+ }
+ .controls {
+ margin-top: 1rem;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="orrery"></canvas>
+ <div class="controls">
+ <input type="range" min="1" max="30" value="10" step="1" id="speedSlider" />
+ <button onclick="togglePause()">Pause</button>
+ </div>
+ <script>
+ const canvas = document.getElementById('orrery');
+ const ctx = canvas.getContext('2d');
+ const width = canvas.width = 600;
+ const height = canvas.height = 600;
+ const planets = [
+ { name: 'Mercury', radius: 1.5, dist: 18, speed: 3.5 },
+ { name: 'Venus', radius: 2.7, dist: 38, speed: 2.9 },
+ { name: 'Earth', radius: 4, dist: 60, speed: 2.4 },
+ { name: 'Mars', radius: 3.5, dist: 100, speed: 2.1 },
+ { name: 'Jupiter', radius: 8, dist: 200, speed: 1.6 },
+ { name: 'Saturn', radius: 6, dist: 400, speed: 1.3 },
+ { name: 'Uranus', radius: 5, dist: 700, speed: 1.2 },
+ { name: 'Neptune', radius: 4, dist: 1100, speed: 1 }
+ ];
+ let paused = false;
+ let time = Date.now();
+
+ function drawPlanet(planet) {
+ const angle = (time + planet.dist * 1000) / 1000 / 60 / 3.142 * 2 * Math.PI;
+ ctx.beginPath();
+ ctx.arc(width / 2, height / 2, planet.dist + planet.radius, 0, 2 * Math.PI);
+ ctx.stroke();
+ ctx.fillStyle = 'white';
+ ctx.save();
+ ctx.translate(width / 2 + planet.dist * Math.cos(angle), height / 2 + planet.dist * Math.sin(angle));
+ ctx.rotate(angle);
+ ctx.arc(planet.radius, 0, planet.radius, 0, 2 * Math.PI);
+ ctx.fill();
+ ctx.restore();
+ ctx.fillStyle = 'white';
+ ctx.fillText(planet.name, width / 2 + planet.dist * Math.cos(angle) - 5, height / 2 + planet.dist * Math.sin(angle));
+ }
+
+ function drawOrbitalRing(planet) {
+ const angle = (time + planet.dist * 1000) / 1000 / 60 / 3.142 * 2 * Math.PI;
+ ctx.beginPath();
+ ctx.arc(width / 2, height / 2, planet.dist - planet.radius, 0, 2 * Math.PI);
+ ctx.stroke();
+ }
+
+ function draw() {
+ ctx.clearRect(0, 0, width, height);
+ planets.forEach(drawPlanet);
+ planets.forEach(drawOrbitalRing);
+ requestAnimationFrame(draw);
+ }
+
+ document.getElementById('speedSlider').oninput = (event) => {
+ const speedValue = event.target.value;
+ planets.forEach(p => p.speed = speedValue / 10 + 2.4);
+ };
+
+ function togglePause() {
+ paused = !paused;
+ }
+
+ draw();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2ca024f41f3c/qwen25-7b.png b/data/artifacts/2ca024f41f3c/qwen25-7b.png
new file mode 100644
index 0000000..4fc7fac
Binary files /dev/null and b/data/artifacts/2ca024f41f3c/qwen25-7b.png differ
diff --git a/data/artifacts/2ca024f41f3c/qwen3-14b.html b/data/artifacts/2ca024f41f3c/qwen3-14b.html
new file mode 100644
index 0000000..12133c0
--- /dev/null
+++ b/data/artifacts/2ca024f41f3c/qwen3-14b.html
@@ -0,0 +1,196 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>Solar System Orrery</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ background: black;
+ color: white;
+ font-family: sans-serif;
+ }
+ canvas {
+ display: block;
+ }
+ #controls {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ background: rgba(0, 0, 0, 0.5);
+ padding: 10px;
+ border-radius: 8px;
+ }
+ #controls label {
+ display: block;
+ margin: 5px 0;
+ }
+ #controls input[type="range"] {
+ width: 200px;
+ }
+ #controls button {
+ margin-top: 10px;
+ padding: 5px 10px;
+ cursor: pointer;
+ }
+ </style>
+</head>
+<body>
+<canvas id="orrery"></canvas>
+<div id="controls">
+ <label>Orbit Speed: <input type="range" id="speedSlider" min="0.1" max="5" step="0.1" value="1" /></label>
+ <button id="pauseBtn">Pause</button>
+</div>
+<script>
+ const canvas = document.getElementById('orrery');
+ const ctx = canvas.getContext('2d');
+ let width, height;
+ let isPaused = false;
+ let speedMultiplier = 1;
+
+ const planets = [
+ {
+ name: 'Mercury',
+ radius: 2,
+ distance: 40,
+ color: '#a9a9a9',
+ orbitColor: '#a9a9a9',
+ speed: 4.5
+ },
+ {
+ name: 'Venus',
+ radius: 3,
+ distance: 70,
+ color: '#f4c430',
+ orbitColor: '#f4c430',
+ speed: 3
+ },
+ {
+ name: 'Earth',
+ radius: 4,
+ distance: 100,
+ color: '#1e90ff',
+ orbitColor: '#1e90ff',
+ speed: 2.5
+ },
+ {
+ name: 'Mars',
+ radius: 3,
+ distance: 140,
+ color: '#d24d57',
+ orbitColor: '#d24d57',
+ speed: 2
+ },
+ {
+ name: 'Jupiter',
+ radius: 6,
+ distance: 200,
+ color: '#f0c419',
+ orbitColor: '#f0c419',
+ speed: 1.5
+ },
+ {
+ name: 'Saturn',
+ radius: 5,
+ distance: 260,
+ color: '#e67e22',
+ orbitColor: '#e67e22',
+ speed: 1.2
+ },
+ {
+ name: 'Uranus',
+ radius: 4,
+ distance: 320,
+ color: '#45b7d1',
+ orbitColor: '#45b7d1',
+ speed: 1
+ },
+ {
+ name: 'Neptune',
+ radius: 4,
+ distance: 380,
+ color: '#2962ff',
+ orbitColor: '#2962ff',
+ speed: 0.9
+ }
+ ];
+
+ const sun = {
+ radius: 15,
+ color: '#ffcc00'
+ };
+
+ let angle = 0;
+ let lastTime = 0;
+
+ function resize() {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ canvas.style.width = width + 'px';
+ canvas.style.height = height + 'px';
+ }
+
+ window.addEventListener('resize', resize);
+ resize();
+
+ function drawOrbit(x, y, radius, color) {
+ ctx.beginPath();
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
+ ctx.strokeStyle = color;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+
+ function drawPlanet(x, y, radius, color, name) {
+ ctx.beginPath();
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
+ ctx.fillStyle = color;
+ ctx.fill();
+ ctx.font = '12px sans-serif';
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+ ctx.fillStyle = 'white';
+ ctx.fillText(name, x, y - radius - 5);
+ }
+
+ function animate(time) {
+ if (!lastTime) lastTime = time;
+ const delta = (time - lastTime) / 1000;
+
+ if (!isPaused) {
+ angle += speedMultiplier * delta;
+ }
+
+ ctx.clearRect(0, 0, width, height);
+
+ // Draw Sun
+ const centerX = width / 2;
+ const centerY = height / 2;
+ drawPlanet(centerX, centerY, sun.radius, sun.color, '');
+
+ // Draw Orbits and Planets
+ for (const planet of planets) {
+ const x = centerX + planet.distance * Math.cos(angle * planet.speed);
+ const y = centerY + planet.distance * Math.sin(angle * planet.speed);
+ drawOrbit(centerX, centerY, planet.distance, planet.orbitColor);
+ drawPlanet(x, y, planet.radius, planet.color, planet.name);
+ }
+
+ lastTime = time;
+ requestAnimationFrame(animate);
+ }
+
+ document.getElementById('speedSlider').addEventListener('input', (e) => {
+ speedMultiplier = parseFloat(e.target.value);
+ });
+
+ document.getElementById('pauseBtn').addEventListener('click', () => {
+ isPaused = !isPaused;
+ document.getElementById('pauseBtn').textContent = isPaused ? 'Resume' : 'Pause';
+ });
+
+ animate(0);
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2ca024f41f3c/qwen3-14b.png b/data/artifacts/2ca024f41f3c/qwen3-14b.png
new file mode 100644
index 0000000..76446b7
Binary files /dev/null and b/data/artifacts/2ca024f41f3c/qwen3-14b.png differ
diff --git a/data/artifacts/2e0c0d48120e/gemma3-12b.html b/data/artifacts/2e0c0d48120e/gemma3-12b.html
new file mode 100644
index 0000000..b7a8d12
--- /dev/null
+++ b/data/artifacts/2e0c0d48120e/gemma3-12b.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>Starfield Warp</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: black; }
+ canvas { display: block; }
+ </style>
+</head>
+<body>
+ <canvas id="starCanvas"></canvas>
+ <script>
+ const canvas = document.getElementById('starCanvas');
+ const ctx = canvas.getContext('2d');
+
+ let width = window.innerWidth;
+ let height = window.innerHeight;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ let stars = [];
+ const numStars = 500; // Adjust for performance
+
+ let warpSpeed = 1;
+ let hyperjumpCharge = 0;
+ const hyperjumpStrength = 20;
+ const hyperjumpDuration = 0.3; // Seconds
+ let motionBlurEnabled = false;
+
+
+ function init() {
+ for (let i = 0; i < numStars; i++) {
+ stars.push({
+ x: Math.random() * width,
+ y: Math.random() * height,
+ z: Math.random() * 100, // Depth value
+ speed: (Math.random() - 0.5) * warpSpeed,
+ });
+ }
+ }
+
+ function drawStar(star) {
+ const brightness = star.z / 100; // Scale depth to brightness
+ ctx.fillStyle = `rgba(255, 255, 255, ${brightness})`;
+ ctx.fillRect(star.x, star.y, 1, 1);
+ }
+
+ function update() {
+ if (motionBlurEnabled) {
+ ctx.globalCompositeOperation = 'multiply'; // Motion blur effect
+ } else {
+ ctx.globalCompositeOperation = 'source-over';
+ }
+
+
+ ctx.clearRect(0, 0, width, height);
+
+ for (let i = 0; i < stars.length; i++) {
+ stars[i].y += stars[i].speed;
+
+ if (stars[i].y > height) {
+ stars[i].y = -Math.random() * height; // Reuse from above screen
+ stars[i].x = Math.random() * width;
+ }
+
+ drawStar(stars[i]);
+ }
+
+
+ requestAnimationFrame(update);
+ }
+
+ canvas.addEventListener('mousemove', (e) => {
+ warpSpeed = Math.max(0, Math.min(10, 5 - e.clientY / window.innerHeight)); //Control warp speed by mouse Y
+ });
+
+ let clickTimer;
+
+ canvas.addEventListener('click', () => {
+ if (!clickTimer) {
+ hyperjumpCharge = hyperjumpStrength;
+ motionBlurEnabled = true;
+ clickTimer = setTimeout(() => {
+ motionBlurEnabled = false;
+ clickTimer = null;
+ }, hyperjumpDuration * 1000);
+ }
+ });
+
+ init();
+ update();
+
+ window.addEventListener('resize', () => {
+ width = window.innerWidth;
+ height = window.innerHeight;
+ canvas.width = width;
+ canvas.height = height;
+ stars = []; // Reinitialize stars on resize
+ numStars = Math.max(50, Math.min(1000, Math.floor((width * height) / 6000)));
+ init();
+ });
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2e0c0d48120e/gemma3-12b.png b/data/artifacts/2e0c0d48120e/gemma3-12b.png
new file mode 100644
index 0000000..ad57eb4
Binary files /dev/null and b/data/artifacts/2e0c0d48120e/gemma3-12b.png differ
diff --git a/data/artifacts/2e0c0d48120e/hermes3-8b.html b/data/artifacts/2e0c0d48120e/hermes3-8b.html
new file mode 100644
index 0000000..4d7fd0c
--- /dev/null
+++ b/data/artifacts/2e0c0d48120e/hermes3-8b.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>3D Starfield Warp Effect</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ touch-action: none;
+ cursor: crosshair;
+ }
+
+ canvas {
+ display: block;
+ }
+ </style>
+</head>
+<body>
+ <canvas></canvas>
+
+ <script>
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+
+ Object.assign(canvas.style, {
+ width: '100%',
+ height: `${window.innerHeight * 0.8}px`
+ });
+
+ document.body.appendChild(canvas);
+
+ let stars = [];
+ const numStars = 500;
+ for (let i = 0; i < numStars; i++) {
+ stars.push({
+ x: Math.random() * window.innerWidth,
+ y: Math.random() * window.innerHeight,
+ speedY: Math.random() * 3 + 1
+ });
+ }
+
+ function render() {
+ 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, Math.random() * 2 + 1, 0, 2 * Math.PI);
+ ctx.fillStyle = `rgba(255, 255, ${Math.random() * 200}, 0.8)`;
+ ctx.fill();
+
+ star.y += star.speedY;
+
+ if (star.y > canvas.height) {
+ star.y = 0;
+ star.x = Math.random() * window.innerWidth;
+ }
+ }
+
+ requestAnimationFrame(render);
+ }
+
+ render();
+
+ document.addEventListener('mousemove', e => {
+ const speedModifier = e.clientY / window.innerHeight;
+ stars.forEach(star => star.speedY += speedModifier);
+ });
+
+ canvas.addEventListener('click', () => {
+ ctx.fillStyle = 'rgba(255, 255, 0, 0.5)';
+ ctx.fillRect(canvas.width / 2 - 10, canvas.height / 2 - 10, 20, 20);
+ requestAnimationFrame(() => {
+ ctx.clearRect(canvas.width / 2 - 11, canvas.height / 2 - 11, 22, 22);
+ });
+ });
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2e0c0d48120e/hermes3-8b.png b/data/artifacts/2e0c0d48120e/hermes3-8b.png
new file mode 100644
index 0000000..6b7addf
Binary files /dev/null and b/data/artifacts/2e0c0d48120e/hermes3-8b.png differ
diff --git a/data/artifacts/2e0c0d48120e/qwen3-14b.html b/data/artifacts/2e0c0d48120e/qwen3-14b.html
new file mode 100644
index 0000000..da2d7ce
--- /dev/null
+++ b/data/artifacts/2e0c0d48120e/qwen3-14b.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>3D Starfield Warp</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: black;
+ height: 100%;
+ width: 100%;
+ }
+ canvas {
+ display: block;
+ }
+ </style>
+</head>
+<body>
+<canvas id="starfield"></canvas>
+<script>
+ const canvas = document.getElementById("starfield");
+ const ctx = canvas.getContext("2d");
+
+ let width, height;
+ let stars = [];
+ let mouse = { y: 0 };
+ let hyperjump = false;
+ let hyperjumpTimer = 0;
+
+ function resize() {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ }
+
+ window.addEventListener("resize", resize);
+ resize();
+
+ window.addEventListener("mousemove", (e) => {
+ mouse.y = e.clientY / height;
+ });
+
+ window.addEventListener("click", () => {
+ hyperjump = true;
+ hyperjumpTimer = 100;
+ });
+
+ class Star {
+ constructor() {
+ this.x = Math.random() * width;
+ this.y = Math.random() * height;
+ this.z = Math.random() * 100 + 50;
+ this.size = Math.random() * 2 + 1;
+ this.speed = Math.random() * 0.1 + 0.02;
+ }
+
+ update() {
+ this.z -= this.speed * (1 + (hyperjump ? 2 : 0));
+ if (this.z < 0) {
+ this.z = 100 + Math.random() * 50;
+ this.x = Math.random() * width;
+ this.y = Math.random() * height;
+ }
+ }
+
+ draw() {
+ const scale = 100 / this.z;
+ const x = this.x * scale + width / 2;
+ const y = this.y * scale + height / 2;
+ const size = this.size * scale;
+ ctx.fillStyle = "white";
+ ctx.beginPath();
+ ctx.arc(x, y, size, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ for (let i = 0; i < 500; i++) {
+ stars.push(new Star());
+ }
+
+ function animate() {
+ ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
+ ctx.fillRect(0, 0, width, height);
+
+ stars.forEach((star) => {
+ star.update();
+ star.draw();
+ });
+
+ hyperjumpTimer--;
+ if (hyperjumpTimer <= 0) hyperjump = false;
+
+ requestAnimationFrame(animate);
+ }
+
+ animate();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/2e0c0d48120e/qwen3-14b.png b/data/artifacts/2e0c0d48120e/qwen3-14b.png
new file mode 100644
index 0000000..58c6850
Binary files /dev/null and b/data/artifacts/2e0c0d48120e/qwen3-14b.png differ
diff --git a/data/artifacts/6acc7c672157/claude-code.html b/data/artifacts/6acc7c672157/claude-code.html
new file mode 100644
index 0000000..97b05cc
--- /dev/null
+++ b/data/artifacts/6acc7c672157/claude-code.html
@@ -0,0 +1,321 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Constellation</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ overflow: hidden;
+ background: #06080f;
+ }
+ canvas {
+ display: block;
+ width: 100%;
+ height: 100%;
+ cursor: crosshair;
+ }
+ #hud {
+ position: fixed;
+ left: 14px;
+ bottom: 12px;
+ font: 11px/1.6 -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
+ color: rgba(160, 190, 255, 0.55);
+ letter-spacing: 0.06em;
+ user-select: none;
+ pointer-events: none;
+ text-transform: uppercase;
+ }
+ #mode {
+ position: fixed;
+ right: 14px;
+ bottom: 12px;
+ font: 11px/1.6 -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
+ color: rgba(160, 190, 255, 0.75);
+ letter-spacing: 0.06em;
+ user-select: none;
+ pointer-events: none;
+ text-transform: uppercase;
+ }
+</style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<div id="hud">move: attract · click: burst · space: repel</div>
+<div id="mode">mode: attract</div>
+<script>
+(function () {
+ 'use strict';
+
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
+ var modeEl = document.getElementById('mode');
+
+ var DPR = Math.min(window.devicePixelRatio || 1, 2);
+ var W = 0, H = 0;
+
+ var mouse = { x: -9999, y: -9999, active: false };
+ var repel = false;
+
+ var LINK_DIST = 110;
+ var MOUSE_RADIUS = 160;
+ var MOUSE_FORCE = 0.045;
+ var MAX_SPEED = 2.4;
+ var DRIFT_SPEED = 0.35;
+
+ var particles = [];
+ var baseCount = 0;
+
+ // Spatial grid for neighbor lookups so line-drawing stays O(n) instead of O(n^2)
+ var cellSize = LINK_DIST;
+ var grid = new Map();
+
+ function resize() {
+ W = window.innerWidth;
+ H = window.innerHeight;
+ canvas.width = Math.round(W * DPR);
+ canvas.height = Math.round(H * DPR);
+ ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
+ baseCount = Math.max(60, Math.min(160, Math.round((W * H) / 11000)));
+ balancePopulation();
+ }
+
+ function rand(a, b) { return a + Math.random() * (b - a); }
+
+ function makeParticle(x, y, burst) {
+ var angle = Math.random() * Math.PI * 2;
+ var speed = burst ? rand(1.5, 4.5) : rand(0.08, DRIFT_SPEED);
+ return {
+ x: x !== undefined ? x : Math.random() * W,
+ y: y !== undefined ? y : Math.random() * H,
+ vx: Math.cos(angle) * speed,
+ vy: Math.sin(angle) * speed,
+ r: rand(1.1, 2.4),
+ hue: burst ? rand(180, 320) : rand(195, 240),
+ life: burst ? 1 : -1, // -1 = permanent; burst particles fade out
+ decay: burst ? rand(0.004, 0.010) : 0,
+ tw: Math.random() * Math.PI * 2, // twinkle phase
+ twSpeed: rand(0.01, 0.04)
+ };
+ }
+
+ function balancePopulation() {
+ var permanent = 0;
+ for (var i = 0; i < particles.length; i++) if (particles[i].life < 0) permanent++;
+ while (permanent < baseCount) { particles.push(makeParticle()); permanent++; }
+ if (permanent > baseCount) {
+ var excess = permanent - baseCount;
+ for (var j = particles.length - 1; j >= 0 && excess > 0; j--) {
+ if (particles[j].life < 0) { particles.splice(j, 1); excess--; }
+ }
+ }
+ }
+
+ function spawnBurst(x, y) {
+ var n = 18;
+ // Cap total population so repeated clicks can't tank the framerate
+ var room = Math.max(0, 380 - particles.length);
+ n = Math.min(n, room);
+ for (var i = 0; i < n; i++) particles.push(makeParticle(x, y, true));
+ }
+
+ function cellKey(cx, cy) { return cx + cy * 100000; }
+
+ function rebuildGrid() {
+ grid.clear();
+ for (var i = 0; i < particles.length; i++) {
+ var p = particles[i];
+ var cx = Math.floor(p.x / cellSize);
+ var cy = Math.floor(p.y / cellSize);
+ var key = cellKey(cx, cy);
+ var bucket = grid.get(key);
+ if (!bucket) { bucket = []; grid.set(key, bucket); }
+ bucket.push(i);
+ }
+ }
+
+ function update() {
+ for (var i = particles.length - 1; i >= 0; i--) {
+ var p = particles[i];
+
+ // Mouse attract / repel
+ if (mouse.active) {
+ var dx = mouse.x - p.x;
+ var dy = mouse.y - p.y;
+ var d2 = dx * dx + dy * dy;
+ var R2 = MOUSE_RADIUS * MOUSE_RADIUS;
+ if (d2 < R2 && d2 > 0.01) {
+ var d = Math.sqrt(d2);
+ var strength = (1 - d / MOUSE_RADIUS) * MOUSE_FORCE;
+ var sign = repel ? -1 : 1;
+ p.vx += (dx / d) * strength * sign * 8;
+ p.vy += (dy / d) * strength * sign * 8;
+ }
+ }
+
+ // Gentle friction toward drift speed
+ p.vx *= 0.985;
+ p.vy *= 0.985;
+
+ // Tiny wander so fully damped particles never freeze
+ p.vx += rand(-0.01, 0.01);
+ p.vy += rand(-0.01, 0.01);
+
+ // Speed cap
+ var sp2 = p.vx * p.vx + p.vy * p.vy;
+ var cap = p.life >= 0 ? MAX_SPEED * 2 : MAX_SPEED;
+ if (sp2 > cap * cap) {
+ var sp = Math.sqrt(sp2);
+ p.vx = (p.vx / sp) * cap;
+ p.vy = (p.vy / sp) * cap;
+ }
+
+ p.x += p.vx;
+ p.y += p.vy;
+ p.tw += p.twSpeed;
+
+ // Wrap around edges with a small margin
+ var m = 8;
+ if (p.x < -m) p.x = W + m; else if (p.x > W + m) p.x = -m;
+ if (p.y < -m) p.y = H + m; else if (p.y > H + m) p.y = -m;
+
+ // Burst particle lifecycle
+ if (p.life >= 0) {
+ p.life -= p.decay;
+ if (p.life <= 0) { particles.splice(i, 1); }
+ }
+ }
+ }
+
+ function alphaOf(p) {
+ var twinkle = 0.75 + 0.25 * Math.sin(p.tw);
+ return (p.life >= 0 ? Math.min(1, p.life * 2) : 1) * twinkle;
+ }
+
+ function draw() {
+ ctx.clearRect(0, 0, W, H);
+
+ // Subtle vignette background
+ var g = ctx.createRadialGradient(W / 2, H / 2, 0, W / 2, H / 2, Math.max(W, H) * 0.75);
+ g.addColorStop(0, '#0b1020');
+ g.addColorStop(1, '#06080f');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, W, H);
+
+ rebuildGrid();
+
+ // Lines between nearby particles
+ ctx.lineWidth = 1;
+ var LD2 = LINK_DIST * LINK_DIST;
+ for (var i = 0; i < particles.length; i++) {
+ var p = particles[i];
+ var cx = Math.floor(p.x / cellSize);
+ var cy = Math.floor(p.y / cellSize);
+ for (var ox = -1; ox <= 1; ox++) {
+ for (var oy = -1; oy <= 1; oy++) {
+ var bucket = grid.get(cellKey(cx + ox, cy + oy));
+ if (!bucket) continue;
+ for (var b = 0; b < bucket.length; b++) {
+ var j = bucket[b];
+ if (j <= i) continue;
+ var q = particles[j];
+ var dx = p.x - q.x, dy = p.y - q.y;
+ var d2 = dx * dx + dy * dy;
+ if (d2 < LD2) {
+ var a = (1 - d2 / LD2) * 0.55 * Math.min(alphaOf(p), alphaOf(q));
+ if (a < 0.02) continue;
+ ctx.strokeStyle = 'hsla(' + ((p.hue + q.hue) / 2) + ', 70%, 70%, ' + a + ')';
+ ctx.beginPath();
+ ctx.moveTo(p.x, p.y);
+ ctx.lineTo(q.x, q.y);
+ ctx.stroke();
+ }
+ }
+ }
+ }
+ }
+
+ // Lines from mouse to nearby particles
+ if (mouse.active) {
+ var MR2 = MOUSE_RADIUS * MOUSE_RADIUS;
+ for (var k = 0; k < particles.length; k++) {
+ var pk = particles[k];
+ var mdx = pk.x - mouse.x, mdy = pk.y - mouse.y;
+ var md2 = mdx * mdx + mdy * mdy;
+ if (md2 < MR2) {
+ var ma = (1 - md2 / MR2) * 0.30;
+ ctx.strokeStyle = 'hsla(' + (repel ? 350 : 200) + ', 90%, 75%, ' + ma + ')';
+ ctx.beginPath();
+ ctx.moveTo(mouse.x, mouse.y);
+ ctx.lineTo(pk.x, pk.y);
+ ctx.stroke();
+ }
+ }
+ }
+
+ // Points with soft glow
+ for (var n = 0; n < particles.length; n++) {
+ var pt = particles[n];
+ var al = alphaOf(pt);
+ ctx.fillStyle = 'hsla(' + pt.hue + ', 85%, 78%, ' + al + ')';
+ ctx.beginPath();
+ ctx.arc(pt.x, pt.y, pt.r, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.fillStyle = 'hsla(' + pt.hue + ', 90%, 70%, ' + (al * 0.12) + ')';
+ ctx.beginPath();
+ ctx.arc(pt.x, pt.y, pt.r * 3.2, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ function frame() {
+ update();
+ draw();
+ requestAnimationFrame(frame);
+ }
+
+ function setMouse(x, y) {
+ mouse.x = x;
+ mouse.y = y;
+ mouse.active = true;
+ }
+
+ window.addEventListener('resize', resize);
+
+ canvas.addEventListener('mousemove', function (e) { setMouse(e.clientX, e.clientY); });
+ canvas.addEventListener('mouseleave', function () { mouse.active = false; mouse.x = -9999; mouse.y = -9999; });
+ canvas.addEventListener('mousedown', function (e) {
+ setMouse(e.clientX, e.clientY);
+ spawnBurst(e.clientX, e.clientY);
+ });
+
+ canvas.addEventListener('touchstart', function (e) {
+ e.preventDefault();
+ var t = e.touches[0];
+ setMouse(t.clientX, t.clientY);
+ spawnBurst(t.clientX, t.clientY);
+ }, { passive: false });
+ canvas.addEventListener('touchmove', function (e) {
+ e.preventDefault();
+ var t = e.touches[0];
+ setMouse(t.clientX, t.clientY);
+ }, { passive: false });
+ canvas.addEventListener('touchend', function () { mouse.active = false; });
+
+ window.addEventListener('keydown', function (e) {
+ if (e.code === 'Space') {
+ e.preventDefault();
+ repel = !repel;
+ modeEl.textContent = 'mode: ' + (repel ? 'repel' : 'attract');
+ }
+ });
+
+ resize();
+ frame();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/6acc7c672157/claude-code.png b/data/artifacts/6acc7c672157/claude-code.png
new file mode 100644
index 0000000..d27e4d6
Binary files /dev/null and b/data/artifacts/6acc7c672157/claude-code.png differ
diff --git a/data/artifacts/6acc7c672157/gemma3-12b.html b/data/artifacts/6acc7c672157/gemma3-12b.html
new file mode 100644
index 0000000..87c0f2f
--- /dev/null
+++ b/data/artifacts/6acc7c672157/gemma3-12b.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Particle Constellation</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: #000; }
+ canvas { display: block; }
+ </style>
+</head>
+<body>
+ <canvas id="particlesCanvas"></canvas>
+
+ <script>
+ const canvas = document.getElementById('particlesCanvas');
+ const ctx = canvas.getContext('2d');
+
+ let particles = [];
+ const numParticles = 150;
+ const repulsionStrength = 120;
+ const attractionStrength = 200;
+ const spawnRadius = 80;
+ const spawnAmount = 10;
+ const maxSpeed = 0.7;
+ const minSpeed = 0.3;
+
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+
+ function init() {
+ for (let i = 0; i < numParticles; i++) {
+ particles.push(new Particle());
+ }
+ }
+
+ class Particle {
+ constructor() {
+ this.x = Math.random() * canvas.width;
+ this.y = Math.random() * canvas.height;
+ this.radius = Math.random() * 3 + 1;
+ this.vx = (Math.random() - 0.5) * maxSpeed;
+ this.vy = (Math.random() - 0.5) * maxSpeed;
+ }
+
+ update() {
+ this.x += this.vx;
+ this.y += this.vy;
+
+ if (this.x < 0 || this.x > canvas.width) this.vx *= -1;
+ if (this.y < 0 || this.y > canvas.height) this.vy *= -1;
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fillStyle = '#fff';
+ ctx.fill();
+ }
+ }
+
+
+ function connectParticles() {
+ for (let i = 0; i < particles.length; i++) {
+ for (let j = i + 1; j < particles.length; j++) {
+ const p1 = particles[i];
+ const p2 = particles[j];
+ const distance = Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
+
+ if (distance < 80) {
+ ctx.beginPath();
+ ctx.moveTo(p1.x, p1.y);
+ ctx.lineTo(p2.x, p2.y);
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
+ ctx.lineWidth = Math.max(1, 1 - distance / 80); // Adjust line width based on distance
+ ctx.stroke();
+ }
+ }
+ }
+ }
+
+ function spawnBurst(x, y) {
+ for (let i = 0; i < spawnAmount; i++) {
+ const burstParticle = new Particle();
+ burstParticle.x = x;
+ burstParticle.y = y;
+ burstParticle.radius = Math.random() * 2;
+ burstParticle.vx = (Math.random() - 0.5) * maxSpeed * 2;
+ burstParticle.vy = (Math.random() - 0.5) * maxSpeed * 2;
+
+ particles.push(burstParticle); // Add to particles array for temporary effect
+ }
+ }
+
+
+ let mouseX = 0;
+ let mouseY = 0;
+ document.addEventListener('mousemove', (e) => {
+ mouseX = e.clientX;
+ mouseY = e.clientY;
+ });
+
+ canvas.addEventListener('click', (e) => {
+ spawnBurst(e.clientX, e.clientY);
+ })
+
+
+ function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = 0; i < particles.length; i++) {
+ const particle = particles[i];
+ particle.update();
+
+ // Mouse attraction/repulsion
+ const dx = mouseX - particle.x;
+ const dy = mouseY - particle.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance < 100) {
+ particle.vx += dx / distance * attractionStrength;
+ particle.vy += dy / distance * attractionStrength;
+ } else if (distance < repulsionStrength){
+ particle.vx -= dx / distance * repulsionStrength;
+ particle.vy -= dy / distance * repulsionStrength;
+ }
+
+ particle.draw();
+ }
+
+ connectParticles();
+
+ // Remove particles that have gone too far from the center after spawning
+ particles = particles.filter(p => Math.sqrt((p.x - canvas.width/2) ** 2 + (p.y - canvas.height / 2) ** 2) < canvas.width);
+
+
+ requestAnimationFrame(animate);
+ }
+
+ window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ });
+
+ init();
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/6acc7c672157/gemma3-12b.png b/data/artifacts/6acc7c672157/gemma3-12b.png
new file mode 100644
index 0000000..3b782a5
Binary files /dev/null and b/data/artifacts/6acc7c672157/gemma3-12b.png differ
diff --git a/data/artifacts/6acc7c672157/gpt.html b/data/artifacts/6acc7c672157/gpt.html
new file mode 100644
index 0000000..54823e8
--- /dev/null
+++ b/data/artifacts/6acc7c672157/gpt.html
@@ -0,0 +1,336 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>Interactive Particle Constellation</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: radial-gradient(circle at top, #1b2735 0, #090a0f 55%);
+ height: 100%;
+ width: 100%;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ color: #ddd;
+ }
+ canvas {
+ display: block;
+ position: fixed;
+ inset: 0;
+ }
+ .overlay {
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ padding: 10px 16px;
+ display: flex;
+ justify-content: space-between;
+ font-size: 12px;
+ pointer-events: none;
+ color: rgba(255,255,255,0.5);
+ text-shadow: 0 0 3px rgba(0,0,0,0.8);
+ }
+ .overlay span {
+ margin-right: 12px;
+ }
+ .hint {
+ display: inline-flex;
+ gap: 14px;
+ }
+ @media (max-width: 600px) {
+ .overlay {
+ font-size: 10px;
+ }
+ .hint {
+ flex-direction: column;
+ gap: 2px;
+ }
+ }
+</style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<div class="overlay">
+ <div>Interactive Constellation</div>
+ <div class="hint">
+ <span>Move: attract</span>
+ <span>Shift+Move: repel</span>
+ <span>Click / tap: burst</span>
+ </div>
+</div>
+<script>
+(function() {
+ const canvas = document.getElementById('c');
+ const ctx = canvas.getContext('2d');
+
+ let width = window.innerWidth;
+ let height = window.innerHeight;
+ let dpr = window.devicePixelRatio || 1;
+
+ function resize() {
+ width = window.innerWidth;
+ height = window.innerHeight;
+ dpr = window.devicePixelRatio || 1;
+ canvas.width = width * dpr;
+ canvas.height = height * dpr;
+ canvas.style.width = width + 'px';
+ canvas.style.height = height + 'px';
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ }
+ window.addEventListener('resize', resize, { passive: true });
+ resize();
+
+ const particles = [];
+ const bursts = [];
+ const baseCount = 110;
+ const maxCount = 220;
+
+ const mouse = {
+ x: 0,
+ y: 0,
+ active: false,
+ repel: false
+ };
+
+ window.addEventListener('mousemove', e => {
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+ mouse.active = true;
+ mouse.repel = e.shiftKey;
+ }, { passive: true });
+
+ window.addEventListener('mouseleave', () => {
+ mouse.active = false;
+ }, { passive: true });
+
+ window.addEventListener('touchstart', e => {
+ const t = e.touches[0];
+ mouse.x = t.clientX;
+ mouse.y = t.clientY;
+ mouse.active = true;
+ mouse.repel = false;
+ spawnBurst(mouse.x, mouse.y, true);
+ }, { passive: true });
+
+ window.addEventListener('touchmove', e => {
+ const t = e.touches[0];
+ mouse.x = t.clientX;
+ mouse.y = t.clientY;
+ mouse.active = true;
+ }, { passive: true });
+
+ window.addEventListener('touchend', () => {
+ mouse.active = false;
+ }, { passive: true });
+
+ window.addEventListener('mousedown', e => {
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+ mouse.active = true;
+ mouse.repel = e.shiftKey;
+ spawnBurst(mouse.x, mouse.y, !e.shiftKey);
+ }, { passive: true });
+
+ window.addEventListener('mouseup', () => {
+ mouse.active = false;
+ }, { passive: true });
+
+ class Particle {
+ constructor(x, y) {
+ this.x = x != null ? x : Math.random() * width;
+ this.y = y != null ? y : Math.random() * height;
+ const angle = Math.random() * Math.PI * 2;
+ const speed = 0.1 + Math.random() * 0.55;
+ this.vx = Math.cos(angle) * speed;
+ this.vy = Math.sin(angle) * speed;
+ this.baseSize = 1.1 + Math.random() * 1.4;
+ this.size = this.baseSize;
+ this.life = 1;
+ this.maxLife = 1;
+ this.colorPhase = Math.random();
+ }
+
+ update(dt) {
+ this.x += this.vx * dt;
+ this.y += this.vy * dt;
+
+ // soft wrap
+ const margin = 40;
+ if (this.x < -margin) this.x = width + margin;
+ else if (this.x > width + margin) this.x = -margin;
+ if (this.y < -margin) this.y = height + margin;
+ else if (this.y > height + margin) this.y = -margin;
+
+ // slow pulsation
+ this.size = this.baseSize * (0.7 + 0.3 * Math.sin(performance.now() / 1000 + this.colorPhase * Math.PI * 2));
+ }
+
+ draw(ctx) {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ const glow = 0.6 + 0.4 * Math.sin(this.colorPhase * Math.PI * 2 + performance.now() / 1600);
+ ctx.fillStyle = `rgba(${150 + glow * 70}, ${180 + glow * 40}, 255, ${0.55 + glow * 0.25})`;
+ ctx.fill();
+ }
+ }
+
+ class BurstParticle {
+ constructor(x, y, outward) {
+ this.x = x;
+ this.y = y;
+ const angle = Math.random() * Math.PI * 2;
+ const speed = (outward ? 3.0 : 2.0) + Math.random() * 2.5;
+ this.vx = Math.cos(angle) * speed;
+ this.vy = Math.sin(angle) * speed;
+ this.life = 1;
+ this.decay = 0.015 + Math.random() * 0.02;
+ this.size = 1.2 + Math.random() * 1.8;
+ this.hue = 200 + Math.random() * 40;
+ }
+ update(dt) {
+ this.x += this.vx * dt;
+ this.y += this.vy * dt;
+ this.vx *= 0.97;
+ this.vy *= 0.97;
+ this.life -= this.decay * dt * 0.6;
+ }
+ draw(ctx) {
+ if (this.life <= 0) return;
+ const alpha = Math.max(0, this.life);
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ ctx.fillStyle = `hsla(${this.hue}, 80%, 70%, ${alpha})`;
+ ctx.fill();
+ }
+ get dead() {
+ return this.life <= 0;
+ }
+ }
+
+ function spawnBurst(x, y, outward) {
+ const count = 22 + Math.floor(Math.random() * 18);
+ for (let i = 0; i < count; i++) {
+ bursts.push(new BurstParticle(x, y, outward));
+ }
+ // also re-energize nearby main particles
+ for (let i = 0; i < particles.length; i++) {
+ const p = particles[i];
+ const dx = p.x - x;
+ const dy = p.y - y;
+ const dist2 = dx * dx + dy * dy;
+ const r = 140;
+ if (dist2 < r * r) {
+ const dist = Math.sqrt(dist2) || 1;
+ const f = (r - dist) / r;
+ const strength = outward ? 0.15 : -0.1;
+ p.vx += (dx / dist) * strength * f * 4;
+ p.vy += (dy / dist) * strength * f * 4;
+ }
+ }
+ }
+
+ function initParticles() {
+ particles.length = 0;
+ const count = Math.min(maxCount, Math.floor(baseCount * (width * height) / (1280 * 720)));
+ for (let i = 0; i < count; i++) {
+ particles.push(new Particle());
+ }
+ }
+ initParticles();
+
+ let lastTime = performance.now();
+
+ function animate(now) {
+ requestAnimationFrame(animate);
+ const dt = Math.min(40, now - lastTime);
+ lastTime = now;
+
+ ctx.clearRect(0, 0, width, height);
+
+ // subtle background vignette
+ const grad = ctx.createRadialGradient(width * 0.5, height * 0.2, 0, width * 0.5, height * 0.5, Math.max(width, height));
+ grad.addColorStop(0, 'rgba(15, 25, 45, 0.9)');
+ grad.addColorStop(0.5, 'rgba(5, 8, 15, 0.96)');
+ grad.addColorStop(1, 'rgba(0, 0, 0, 1)');
+ ctx.fillStyle = grad;
+ ctx.fillRect(0, 0, width, height);
+
+ // mouse influence on main particles
+ const influenceRadius = Math.min(width, height) * 0.35;
+ const influenceRadius2 = influenceRadius * influenceRadius;
+ const mouseStrength = 0.0015;
+
+ for (let i = 0; i < particles.length; i++) {
+ const p = particles[i];
+
+ if (mouse.active) {
+ const dx = mouse.x - p.x;
+ const dy = mouse.y - p.y;
+ const dist2 = dx * dx + dy * dy;
+ if (dist2 < influenceRadius2) {
+ const dist = Math.sqrt(dist2) || 1;
+ const forceDir = mouse.repel ? -1 : 1;
+ const f = (1 - dist / influenceRadius) * forceDir;
+ const ax = (dx / dist) * f * mouseStrength * dt * 60;
+ const ay = (dy / dist) * f * mouseStrength * dt * 60;
+ p.vx += ax;
+ p.vy += ay;
+ }
+ }
+ p.update(dt);
+ }
+
+ // draw connecting lines
+ const maxDist = Math.min(width, height) * 0.18;
+ const maxDist2 = maxDist * maxDist;
+ ctx.lineWidth = 0.7;
+ for (let i = 0; i < particles.length; i++) {
+ const p1 = particles[i];
+ for (let j = i + 1; j < particles.length; j++) {
+ const p2 = particles[j];
+ const dx = p1.x - p2.x;
+ const dy = p1.y - p2.y;
+ const dist2 = dx * dx + dy * dy;
+ if (dist2 < maxDist2) {
+ const dist = Math.sqrt(dist2);
+ let alpha = 1 - dist / maxDist;
+ if (mouse.active) {
+ const mx = (p1.x + p2.x) * 0.5 - mouse.x;
+ const my = (p1.y + p2.y) * 0.5 - mouse.y;
+ const mDist2 = mx * mx + my * my;
+ const mR = influenceRadius2 * 0.7;
+ if (mDist2 < mR) {
+ alpha += 0.3 * (1 - mDist2 / mR);
+ }
+ }
+ alpha = Math.min(0.85, Math.max(0, alpha));
+ ctx.strokeStyle = `rgba(160,190,255,${0.18 * alpha})`;
+ ctx.beginPath();
+ ctx.moveTo(p1.x, p1.y);
+ ctx.lineTo(p2.x, p2.y);
+ ctx.stroke();
+ }
+ }
+ }
+
+ // draw main particles
+ for (let i = 0; i < particles.length; i++) {
+ particles[i].draw(ctx);
+ }
+
+ // update & draw bursts
+ for (let i = bursts.length - 1; i >= 0; i--) {
+ const b = bursts[i];
+ b.update(dt);
+ b.draw(ctx);
+ if (b.dead) bursts.splice(i, 1);
+ }
+ }
+
+ requestAnimationFrame(animate);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/6acc7c672157/gpt.png b/data/artifacts/6acc7c672157/gpt.png
new file mode 100644
index 0000000..7791a57
Binary files /dev/null and b/data/artifacts/6acc7c672157/gpt.png differ
diff --git a/data/artifacts/6acc7c672157/grok.html b/data/artifacts/6acc7c672157/grok.html
new file mode 100644
index 0000000..38f3ee4
--- /dev/null
+++ b/data/artifacts/6acc7c672157/grok.html
@@ -0,0 +1,213 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Particle Constellation</title>
+<style>
+*{margin:0;padding:0;box-sizing:border-box}
+html,body{width:100%;height:100%;overflow:hidden;background:#05050a}
+canvas{display:block;width:100%;height:100%;cursor:crosshair}
+</style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<script>
+(function(){
+const canvas=document.getElementById('c');
+const ctx=canvas.getContext('2d');
+let W,H,dpr,particles=[],mouse={x:-9999,y:-9999,active:false,repel:false};
+const COUNT=120;
+const CONNECT_DIST=140;
+const MOUSE_RADIUS=180;
+const MAX_SPEED=1.4;
+
+function resize(){
+ dpr=Math.min(window.devicePixelRatio||1,2);
+ W=window.innerWidth;
+ H=window.innerHeight;
+ canvas.width=W*dpr;
+ canvas.height=H*dpr;
+ canvas.style.width=W+'px';
+ canvas.style.height=H+'px';
+ ctx.setTransform(dpr,0,0,dpr,0,0);
+}
+
+function rand(a,b){return a+Math.random()*(b-a)}
+
+function makeParticle(x,y,burst){
+ const angle=Math.random()*Math.PI*2;
+ const speed=burst?rand(2,6):rand(0.2,MAX_SPEED);
+ return{
+ x:x??rand(0,W),
+ y:y??rand(0,H),
+ vx:Math.cos(angle)*speed,
+ vy:Math.sin(angle)*speed,
+ r:burst?rand(1.5,3.2):rand(1.2,2.4),
+ life:burst?1:Infinity,
+ decay:burst?rand(0.008,0.02):0,
+ hue:rand(190,260)
+ };
+}
+
+function init(){
+ particles=[];
+ for(let i=0;i<COUNT;i++) particles.push(makeParticle());
+}
+
+function spawnBurst(x,y,n){
+ for(let i=0;i<n;i++) particles.push(makeParticle(x,y,true));
+}
+
+function update(){
+ const mx=mouse.x,my=mouse.y;
+ for(let i=particles.length-1;i>=0;i--){
+ const p=particles[i];
+ if(p.life!==Infinity){
+ p.life-=p.decay;
+ if(p.life<=0){particles.splice(i,1);continue;}
+ }
+ if(mouse.active){
+ const dx=mx-p.x,dy=my-p.y;
+ const dist=Math.hypot(dx,dy)||1;
+ if(dist<MOUSE_RADIUS){
+ const force=(1-dist/MOUSE_RADIUS)*0.08;
+ const dir=mouse.repel?-1:1;
+ p.vx+= (dx/dist)*force*dir;
+ p.vy+= (dy/dist)*force*dir;
+ }
+ }
+ p.vx*=0.995;
+ p.vy*=0.995;
+ const sp=Math.hypot(p.vx,p.vy);
+ if(sp>MAX_SPEED*2){p.vx=(p.vx/sp)*MAX_SPEED*2;p.vy=(p.vy/sp)*MAX_SPEED*2;}
+ if(sp<0.15&&p.life===Infinity){
+ p.vx+=rand(-0.05,0.05);
+ p.vy+=rand(-0.05,0.05);
+ }
+ p.x+=p.vx;
+ p.y+=p.vy;
+ if(p.x<-20)p.x=W+20;else if(p.x>W+20)p.x=-20;
+ if(p.y<-20)p.y=H+20;else if(p.y>H+20)p.y=-20;
+ }
+ if(particles.length>COUNT+80){
+ particles=particles.filter(p=>p.life===Infinity||p.life>0.3).slice(-COUNT-60);
+ }
+}
+
+function draw(){
+ ctx.fillStyle='rgba(5,5,10,0.28)';
+ ctx.fillRect(0,0,W,H);
+ const n=particles.length;
+ const cd2=CONNECT_DIST*CONNECT_DIST;
+ for(let i=0;i<n;i++){
+ const a=particles[i];
+ for(let j=i+1;j<n;j++){
+ const b=particles[j];
+ const dx=a.x-b.x,dy=a.y-b.y;
+ const d2=dx*dx+dy*dy;
+ if(d2<cd2){
+ const t=1-Math.sqrt(d2)/CONNECT_DIST;
+ const alpha=t*t*0.55*(Math.min(a.life,b.life,1));
+ ctx.beginPath();
+ ctx.strokeStyle=`hsla(210,70%,70%,${alpha})`;
+ ctx.lineWidth=t*1.2;
+ ctx.moveTo(a.x,a.y);
+ ctx.lineTo(b.x,b.y);
+ ctx.stroke();
+ }
+ }
+ }
+ if(mouse.active){
+ for(let i=0;i<n;i++){
+ const p=particles[i];
+ const dx=mouse.x-p.x,dy=mouse.y-p.y;
+ const d2=dx*dx+dy*dy;
+ const mr2=MOUSE_RADIUS*MOUSE_RADIUS;
+ if(d2<mr2){
+ const t=1-Math.sqrt(d2)/MOUSE_RADIUS;
+ ctx.beginPath();
+ ctx.strokeStyle=`hsla(200,90%,75%,${t*0.25})`;
+ ctx.lineWidth=0.6;
+ ctx.moveTo(p.x,p.y);
+ ctx.lineTo(mouse.x,mouse.y);
+ ctx.stroke();
+ }
+ }
+ }
+ for(let i=0;i<n;i++){
+ const p=particles[i];
+ const alpha=p.life===Infinity?0.9:p.life;
+ const g=ctx.createRadialGradient(p.x,p.y,0,p.x,p.y,p.r*4);
+ g.addColorStop(0,`hsla(${p.hue},80%,75%,${alpha*0.9})`);
+ g.addColorStop(0.4,`hsla(${p.hue},70%,60%,${alpha*0.35})`);
+ g.addColorStop(1,`hsla(${p.hue},60%,50%,0)`);
+ ctx.beginPath();
+ ctx.fillStyle=g;
+ ctx.arc(p.x,p.y,p.r*4,0,Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.fillStyle=`hsla(${p.hue},90%,85%,${alpha})`;
+ ctx.arc(p.x,p.y,p.r,0,Math.PI*2);
+ ctx.fill();
+ }
+ if(mouse.active){
+ const rg=ctx.createRadialGradient(mouse.x,mouse.y,0,mouse.x,mouse.y,MOUSE_RADIUS);
+ rg.addColorStop(0,mouse.repel?'rgba(255,100,120,0.06)':'rgba(100,180,255,0.07)');
+ rg.addColorStop(1,'rgba(0,0,0,0)');
+ ctx.fillStyle=rg;
+ ctx.beginPath();
+ ctx.arc(mouse.x,mouse.y,MOUSE_RADIUS,0,Math.PI*2);
+ ctx.fill();
+ }
+}
+
+function loop(){
+ update();
+ draw();
+ requestAnimationFrame(loop);
+}
+
+window.addEventListener('resize',()=>{resize();});
+canvas.addEventListener('mousemove',e=>{
+ mouse.x=e.clientX;mouse.y=e.clientY;mouse.active=true;
+});
+canvas.addEventListener('mouseleave',()=>{mouse.active=false;mouse.x=-9999;mouse.y=-9999;});
+canvas.addEventListener('mousedown',e=>{
+ mouse.repel=e.button===2||e.shiftKey;
+ spawnBurst(e.clientX,e.clientY,18);
+});
+canvas.addEventListener('contextmenu',e=>e.preventDefault());
+canvas.addEventListener('mouseup',()=>{mouse.repel=false;});
+window.addEventListener('keydown',e=>{if(e.key==='Shift')mouse.repel=true;});
+window.addEventListener('keyup',e=>{if(e.key==='Shift')mouse.repel=false;});
+
+let touchId=null;
+canvas.addEventListener('touchstart',e=>{
+ e.preventDefault();
+ const t=e.changedTouches[0];
+ touchId=t.identifier;
+ mouse.x=t.clientX;mouse.y=t.clientY;mouse.active=true;
+ spawnBurst(t.clientX,t.clientY,16);
+},{passive:false});
+canvas.addEventListener('touchmove',e=>{
+ e.preventDefault();
+ for(const t of e.changedTouches){
+ if(t.identifier===touchId){mouse.x=t.clientX;mouse.y=t.clientY;}
+ }
+},{passive:false});
+canvas.addEventListener('touchend',e=>{
+ for(const t of e.changedTouches){
+ if(t.identifier===touchId){mouse.active=false;touchId=null;mouse.x=-9999;mouse.y=-9999;}
+ }
+});
+
+resize();
+init();
+ctx.fillStyle='#05050a';
+ctx.fillRect(0,0,W,H);
+loop();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/6acc7c672157/grok.png b/data/artifacts/6acc7c672157/grok.png
new file mode 100644
index 0000000..7aa9e60
Binary files /dev/null and b/data/artifacts/6acc7c672157/grok.png differ
diff --git a/data/artifacts/6acc7c672157/hermes3-8b.html b/data/artifacts/6acc7c672157/hermes3-8b.html
new file mode 100644
index 0000000..ba4931a
--- /dev/null
+++ b/data/artifacts/6acc7c672157/hermes3-8b.html
@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Interactive Particle Constellation</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ background-color: #000;
+ touch-action: none;
+ user-select: none;
+ cursor: crosshair;
+ }
+
+ .point {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ border-radius: 50%;
+ background-color: #fff;
+ box-shadow:
+ 2px 2px 0px rgba(255, 255, 255, 0.8),
+ calc(2px * 2) px calc(2px * 2) px rgba(255, 255, 255, 0.6);
+ }
+ </style>
+</head>
+<body>
+ <script>
+ const canvas = {
+ width: window.innerWidth,
+ height: window.innerHeight
+ };
+
+ let points = [];
+ const maxPoints = 100;
+ const minDistance = 10;
+ const maxSpeed = 5;
+
+ function random(min, max) {
+ return Math.random() * (max - min) + min;
+ }
+
+ function vector(length, angle) {
+ const x = Math.cos(angle) * length;
+ const y = Math.sin(angle) * length;
+ return {x, y};
+ }
+
+ function distance(p1, p2) {
+ return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
+ }
+
+ function connectPoints(points) {
+ const lines = [];
+ for (let i = 0; i < points.length; i++) {
+ for (let j = i + 1; j < points.length; j++) {
+ if (distance(points[i], points[j]) < minDistance) {
+ lines.push({from: points[i], to: points[j]});
+ }
+ }
+ }
+ return lines;
+ }
+
+ function randomPoints(count) {
+ const points = [];
+ for (let i = 0; i < count; i++) {
+ let point = {x: random(0, canvas.width), y: random(0, canvas.height)};
+ points.push(point);
+ }
+ return points;
+ }
+
+ function attractPoint(point, target) {
+ const dx = target.x - point.x;
+ const dy = target.y - point.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance > minDistance) {
+ const speed = maxSpeed * (minDistance / distance);
+ point.x += dx * speed;
+ point.y += dy * speed;
+ }
+ }
+
+ function repelPoints(point, points) {
+ for (let i = 0; i < points.length; i++) {
+ if (point !== points[i]) {
+ const dx = points[i].x - point.x;
+ const dy = points[i].y - point.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance > minDistance) {
+ const speed = maxSpeed * (minDistance / distance);
+ point.x -= dx * speed;
+ point.y -= dy * speed;
+ }
+ }
+ }
+ }
+
+ function burstPoints(point, count) {
+ for (let i = 0; i < count; i++) {
+ let angle = random(0, Math.PI * 2);
+ const vector = vector(random(minDistance / 2, minDistance), angle);
+ point.x += vector.x;
+ point.y += vector.y;
+ points.push({x: point.x, y: point.y});
+ }
+ }
+
+ function updatePoints() {
+ for (let i = 0; i < points.length; i++) {
+ const point = points[i];
+ repelPoints(point, points);
+ attractPoint(point, {x: mouse.x, y: mouse.y});
+ }
+ while (points.length > maxPoints) {
+ points.shift();
+ }
+ }
+
+ function render() {
+ context.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = 0; i < points.length; i++) {
+ const point = points[i];
+ context.beginPath();
+ context.arc(point.x, point.y, minDistance / 2, 0, Math.PI * 2);
+ context.stroke();
+
+ const connectedPoints = connectPoints(points.slice(i));
+ for (let j = 0; j < connectedPoints.length; j++) {
+ const line = connectedPoints[j];
+ context.beginPath();
+ context.moveTo(line.from.x, line.from.y);
+ context.lineTo(line.to.x, line.to.y);
+ context.stroke();
+ }
+ }
+ }
+
+ const mouse = {x: canvas.width / 2, y: canvas.height / 2};
+ let lastMouse = {x: canvas.width / 2, y: canvas.height / 2};
+
+ const context = document.createElement('canvas').getContext('2d');
+ context.canvas.width = canvas.width;
+ context.canvas.height = canvas.height;
+ document.body.appendChild(context.canvas);
+
+ window.onmousemove = (e) => {
+ lastMouse = {x: e.clientX, y: e.clientY};
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+ };
+
+ window.onclick = () => {
+ burstPoints(mouse, random(5, 10));
+ };
+
+ function loop() {
+ updatePoints();
+ render();
+ requestAnimationFrame(loop);
+ }
+
+ loop();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/6acc7c672157/hermes3-8b.png b/data/artifacts/6acc7c672157/hermes3-8b.png
new file mode 100644
index 0000000..ad57eb4
Binary files /dev/null and b/data/artifacts/6acc7c672157/hermes3-8b.png differ
diff --git a/data/artifacts/6acc7c672157/hf-qwen-coder-32b.html b/data/artifacts/6acc7c672157/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..22d6a32
--- /dev/null
+++ b/data/artifacts/6acc7c672157/hf-qwen-coder-32b.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>Particle Constellation</title>
+ <style>
+ body, canvas { margin: 0; padding: 0; display: block; }
+ body { background-color: #000; overflow: hidden; }
+ </style>
+</head>
+<body>
+<canvas id="canvas"></canvas>
+<script>
+const canvas = document.getElementById('canvas');
+const ctx = canvas.getContext('2d');
+let particles = [];
+let mouse = { x: 0, y: 0 };
+let attract = true;
+
+function resizeCanvas() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+}
+
+window.addEventListener('resize', resizeCanvas);
+resizeCanvas();
+
+canvas.addEventListener('mousemove', (e) => {
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+});
+
+canvas.addEventListener('click', () => {
+ attract = !attract;
+ for (let i = 0; i < 15; i++) {
+ particles.push(new Particle(mouse.x, mouse.y));
+ }
+});
+
+function Particle(x, y) {
+ this.x = x || Math.random() * canvas.width;
+ this.y = y || Math.random() * canvas.height;
+ this.vx = (Math.random() - 0.5) * 2;
+ this.vy = (Math.random() - 0.5) * 2;
+ this.size = Math.random() * 2 + 1;
+}
+
+Particle.prototype.update = function() {
+ let dx = mouse.x - this.x;
+ let dy = mouse.y - this.y;
+ let distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < 150) {
+ let forceX = dx / distance;
+ let forceY = dy / distance;
+ let direction = attract ? 1 : -1;
+ this.vx += forceX * direction * 0.3;
+ this.vy += forceY * direction * 0.3;
+ }
+ this.x += this.vx;
+ this.y += this.vy;
+
+ if (this.x < 0 || this.x > canvas.width) {
+ this.vx *= -1;
+ }
+ if (this.y < 0 || this.y > canvas.height) {
+ this.vy *= -1;
+ }
+
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ ctx.fillStyle = '#fff';
+ ctx.fill();
+
+ for (let i = particles.length - 1; i >= 0; i--) {
+ let p2 = particles[i];
+ if (this !== p2) {
+ let dx = this.x - p2.x;
+ let dy = this.y - p2.y;
+ let distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < 100) {
+ ctx.beginPath();
+ ctx.moveTo(this.x, this.y);
+ ctx.lineTo(p2.x, p2.y);
+ ctx.strokeStyle = 'rgba(255,255,255,' + (1 - distance / 100) + ')';
+ ctx.stroke();
+ }
+ }
+ }
+
+ if (Math.abs(this.vx) < 0.1 && Math.abs(this.vy) < 0.1) {
+ this.vx = (Math.random() - 0.5) * 2;
+ this.vy = (Math.random() - 0.5) * 2;
+ }
+};
+
+function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for (let i = particles.length - 1; i >= 0; i--) {
+ particles[i].update();
+ }
+ requestAnimationFrame(animate);
+}
+
+for (let i = 0; i < 50; i++) {
+ particles.push(new Particle());
+}
+
+animate();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/6acc7c672157/hf-qwen-coder-32b.png b/data/artifacts/6acc7c672157/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..7264a18
Binary files /dev/null and b/data/artifacts/6acc7c672157/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/6acc7c672157/kimi.html b/data/artifacts/6acc7c672157/kimi.html
new file mode 100644
index 0000000..9c6ba2e
--- /dev/null
+++ b/data/artifacts/6acc7c672157/kimi.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>Particle Constellation</title>
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+ body {
+ overflow: hidden;
+ background: #000;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ }
+ canvas {
+ display: block;
+ background: radial-gradient(circle at center, #0a0a1a 0%, #000000 100%);
+ }
+ #info {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 13px;
+ letter-spacing: 0.5px;
+ pointer-events: none;
+ user-select: none;
+ z-index: 10;
+ }
+ #mode {
+ position: absolute;
+ top: 20px;
+ right: 20px;
+ color: rgba(100, 200, 255, 0.8);
+ font-size: 13px;
+ font-weight: 600;
+ pointer-events: none;
+ user-select: none;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="canvas"></canvas>
+ <div id="info">Click to spawn • Space to toggle mode</div>
+ <div id="mode">Attract</div>
+
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const modeDisplay = document.getElementById('mode');
+
+ let width, height;
+ let particles = [];
+ const baseParticleCount = 100;
+ const connectionDistance = 120;
+ const mouseRadius = 150;
+ let mouse = { x: null, y: null };
+ let interactionMode = 'attract'; // 'attract' or 'repel'
+
+ function resize() {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ }
+
+ window.addEventListener('resize', resize);
+ resize();
+
+ class Particle {
+ constructor(x, y, burst = false) {
+ this.x = x || Math.random() * width;
+ this.y = y || Math.random() * height;
+ this.vx = (Math.random() - 0.5) * (burst ? 4 : 0.8);
+ this.vy = (Math.random() - 0.5) * (burst ? 4 : 0.8);
+ this.radius = Math.random() * 1.5 + 0.5;
+ this.friction = 0.98;
+ this.baseSpeed = 0.3;
+ this.life = burst ? 1.0 : null; // For burst particles
+ }
+
+ update() {
+ // Mouse interaction
+ if (mouse.x !== null) {
+ const dx = mouse.x - this.x;
+ const dy = mouse.y - this.y;
+ const distSq = dx * dx + dy * dy;
+
+ if (distSq < mouseRadius * mouseRadius && distSq > 0) {
+ const dist = Math.sqrt(distSq);
+ const force = (mouseRadius - dist) / mouseRadius;
+ const angle = Math.atan2(dy, dx);
+ const strength = 0.8;
+
+ if (interactionMode === 'attract') {
+ this.vx += Math.cos(angle) * force * strength;
+ this.vy += Math.sin(angle) * force * strength;
+ } else {
+ this.vx -= Math.cos(angle) * force * strength;
+ this.vy -= Math.sin(angle) * force * strength;
+ }
+ }
+ }
+
+ // Apply physics
+ this.x += this.vx;
+ this.y += this.vy;
+
+ // Friction
+ this.vx *= this.friction;
+ this.vy *= this.friction;
+
+ // Base drift
+ if (Math.abs(this.vx) < this.baseSpeed) this.vx += (Math.random() - 0.5) * 0.1;
+ if (Math.abs(this.vy) < this.baseSpeed) this.vy += (Math.random() - 0.5) * 0.1;
+
+ // Wrap around edges
+ if (this.x < 0) this.x = width;
+ else if (this.x > width) this.x = 0;
+ if (this.y < 0) this.y = height;
+ else if (this.y > height) this.y = 0;
+
+ // Fade burst particles
+ if (this.life !== null) {
+ this.life -= 0.01;
+ }
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fillStyle = this.life !== null ?
+ `rgba(150, 220, 255, ${this.life})` :
+ 'rgba(200, 230, 255, 0.8)';
+ ctx.fill();
+ }
+ }
+
+ function init() {
+ particles = [];
+ for (let i = 0; i < baseParticleCount; i++) {
+ particles.push(new Particle());
+ }
+ }
+
+ function drawConnections() {
+ ctx.lineWidth = 0.8;
+
+ for (let i = 0; i < particles.length; i++) {
+ for (let j = i + 1; j < particles.length; j++) {
+ const dx = particles[i].x - particles[j].x;
+ const dy = particles[i].y - particles[j].y;
+ const distSq = dx * dx + dy * dy;
+
+ if (distSq < connectionDistance * connectionDistance) {
+ const dist = Math.sqrt(distSq);
+ const opacity = (1 - dist / connectionDistance) * 0.5;
+
+ ctx.strokeStyle = `rgba(120, 180, 255, ${opacity})`;
+ ctx.beginPath();
+ ctx.moveTo(particles[i].x, particles[i].y);
+ ctx.lineTo(particles[j].x, particles[j].y);
+ ctx.stroke();
+ }
+ }
+ }
+ }
+
+ function animate() {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
+ ctx.fillRect(0, 0, width, height);
+
+ // Update and draw particles
+ for (let i = particles.length - 1; i >= 0; i--) {
+ const p = particles[i];
+ p.update();
+ p.draw();
+
+ // Remove dead burst particles
+ if (p.life !== null && p.life <= 0) {
+ particles.splice(i, 1);
+ }
+ }
+
+ drawConnections();
+ requestAnimationFrame(animate);
+ }
+
+ // Input handling
+ window.addEventListener('mousemove', (e) => {
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+ });
+
+ window.addEventListener('mouseleave', () => {
+ mouse.x = null;
+ mouse.y = null;
+ });
+
+ window.addEventListener('click', (e) => {
+ // Spawn burst
+ for (let i = 0; i < 10; i++) {
+ particles.push(new Particle(e.clientX, e.clientY, true));
+ }
+
+ // Cap total particles
+ if (particles.length > 250) {
+ particles.splice(0, particles.length - 250);
+ }
+ });
+
+ window.addEventListener('keydown', (e) => {
+ if (e.code === 'Space') {
+ interactionMode = interactionMode === 'attract' ? 'repel' : 'attract';
+ modeDisplay.textContent = interactionMode;
+ modeDisplay.style.color = interactionMode === 'attract' ?
+ 'rgba(100, 200, 255, 0.8)' :
+ 'rgba(255, 100, 150, 0.8)';
+ }
+ });
+
+ // Touch support
+ window.addEventListener('touchmove', (e) => {
+ e.preventDefault();
+ mouse.x = e.touches[0].clientX;
+ mouse.y = e.touches[0].clientY;
+ }, { passive: false });
+
+ window.addEventListener('touchstart', (e) => {
+ mouse.x = e.touches[0].clientX;
+ mouse.y = e.touches[0].clientY;
+ // Spawn on touch
+ for (let i = 0; i < 5; i++) {
+ particles.push(new Particle(mouse.x, mouse.y, true));
+ }
+ });
+
+ window.addEventListener('touchend', () => {
+ mouse.x = null;
+ mouse.y = null;
+ });
+
+ init();
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/6acc7c672157/kimi.png b/data/artifacts/6acc7c672157/kimi.png
new file mode 100644
index 0000000..8e14a9f
Binary files /dev/null and b/data/artifacts/6acc7c672157/kimi.png differ
diff --git a/data/artifacts/6acc7c672157/qwen25-7b.html b/data/artifacts/6acc7c672157/qwen25-7b.html
new file mode 100644
index 0000000..844c26a
--- /dev/null
+++ b/data/artifacts/6acc7c672157/qwen25-7b.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Particle Constellation</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ background-color: black;
+ }
+ canvas {
+ display: block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+ </style>
+</head>
+<body>
+<canvas id="particles"></canvas>
+<script>
+const particles = [];
+const canvas = document.getElementById('particles');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+function Particle(x, y) {
+ this.x = x || Math.random() * innerWidth;
+ this.y = y || Math.random() * innerHeight;
+ this.size = Math.random() * 4 + 2;
+ this.speedX = (Math.random() - 0.5) * 3;
+ this.speedY = (Math.random() - 0.5) * 3;
+}
+
+Particle.prototype.draw = function () {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
+ ctx.fill();
+};
+
+Particle.prototype.update = function (mouse) {
+ let dx = mouse.x - this.x;
+ let dy = mouse.y - this.y;
+ let distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < 100) {
+ this.speedX -= dx / 50;
+ this.speedY -= dy / 50;
+ }
+ this.x += this.speedX;
+ this.y += this.speedY;
+
+ ctx.beginPath();
+ ctx.moveTo(this.x, this.y);
+ for (let i = 0; i < particles.length; i++) {
+ let p2 = particles[i];
+ if (p2 !== this && distance < 80) {
+ ctx.lineTo(p2.x, p2.y);
+ }
+ }
+ ctx.stroke();
+};
+
+Particle.prototype.reset = function () {
+ this.x = Math.random() * innerWidth;
+ this.y = Math.random() * innerHeight;
+ this.size = Math.random() * 4 + 2;
+ this.speedX = (Math.random() - 0.5) * 3;
+ this.speedY = (Math.random() - 0.5) * 3;
+};
+
+window.requestAnimationFrame = window.requestAnimationFrame || function (callback) {
+ return setTimeout(callback, 1000 / 60);
+};
+
+let mouse = { x: canvas.width / 2, y: canvas.height / 2 };
+
+canvas.addEventListener('mousemove', (e) => {
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+});
+
+canvas.addEventListener('click', () => {
+ for (let i = 0; i < 100; i++) {
+ particles.push(new Particle(mouse.x, mouse.y));
+ }
+});
+
+function animate() {
+ ctx.clearRect(0, 0, innerWidth, innerHeight);
+ let mouseDist = Math.sqrt((mouse.x - canvas.width / 2) * (mouse.x - canvas.width / 2) + (mouse.y - canvas.height / 2) * (mouse.y - canvas.height / 2));
+ if (mouseDist > 100) {
+ for (let i = 0; i < particles.length; i++) {
+ particles[i].update(mouse);
+ particles[i].draw();
+ if ((particles[i].x - mouse.x) ** 2 + (particles[i].y - mouse.y) ** 2 < 150 || Math.random() > 0.97) {
+ particles[i].reset();
+ }
+ }
+ }
+}
+
+const ctx = canvas.getContext('2d');
+canvas.addEventListener('click', () => {
+ for (let i = 0; i < 100; i++) {
+ particles.push(new Particle(mouse.x, mouse.y));
+ }
+});
+
+for (let i = 0; i < 300; i++) {
+ particles.push(new Particle());
+}
+
+requestAnimationFrame(animate);
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/6acc7c672157/qwen25-7b.png b/data/artifacts/6acc7c672157/qwen25-7b.png
new file mode 100644
index 0000000..ad57eb4
Binary files /dev/null and b/data/artifacts/6acc7c672157/qwen25-7b.png differ
diff --git a/data/artifacts/6acc7c672157/qwen3-14b.html b/data/artifacts/6acc7c672157/qwen3-14b.html
new file mode 100644
index 0000000..dab0a4b
--- /dev/null
+++ b/data/artifacts/6acc7c672157/qwen3-14b.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Particle Constellation</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #000;
+ width: 100%;
+ height: 100%;
+ }
+ canvas {
+ display: block;
+ }
+ </style>
+</head>
+<body>
+<canvas></canvas>
+<script>
+ const canvas = document.querySelector('canvas');
+ 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 particles = [];
+ const numParticles = 100;
+
+ class Particle {
+ constructor() {
+ this.x = Math.random() * width;
+ this.y = Math.random() * height;
+ this.vx = (Math.random() - 0.5) * 0.5;
+ this.vy = (Math.random() - 0.5) * 0.5;
+ this.radius = 2 + Math.random() * 2;
+ this.color = `hsl(${Math.random() * 360}, 100%, 70%)`;
+ }
+
+ update(mouseX, mouseY) {
+ const dx = mouseX - this.x;
+ const dy = mouseY - this.y;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ const force = 0.05 / (dist * dist + 0.01);
+ this.vx += dx * force;
+ this.vy += dy * force;
+ this.x += this.vx;
+ this.y += this.vy;
+ this.x = Math.max(0, Math.min(width, this.x));
+ this.y = Math.max(0, Math.min(height, this.y));
+ }
+
+ draw(ctx) {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ }
+ }
+
+ for (let i = 0; i < numParticles; i++) {
+ particles.push(new Particle());
+ }
+
+ function drawLines() {
+ for (let i = 0; i < particles.length; i++) {
+ for (let j = i + 1; j < particles.length; j++) {
+ const dx = particles[i].x - particles[j].x;
+ const dy = particles[i].y - particles[j].y;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < 150) {
+ ctx.beginPath();
+ ctx.moveTo(particles[i].x, particles[i].y);
+ ctx.lineTo(particles[j].x, particles[j].y);
+ ctx.strokeStyle = `rgba(255, 255, 255, ${0.1 - dist / 150})`;
+ ctx.stroke();
+ }
+ }
+ }
+ }
+
+ let mouse = { x: width / 2, y: height / 2 };
+ let clickBurst = [];
+
+ canvas.addEventListener('mousemove', e => {
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+ });
+
+ canvas.addEventListener('click', e => {
+ const burstCount = 20;
+ for (let i = 0; i < burstCount; i++) {
+ clickBurst.push({
+ x: e.clientX,
+ y: e.clientY,
+ vx: (Math.random() - 0.5) * 10,
+ vy: (Math.random() - 0.5) * 10,
+ life: 100,
+ color: `hsl(${Math.random() * 360}, 100%, 70%)`
+ });
+ }
+ });
+
+ function animate() {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
+ ctx.fillRect(0, 0, width, height);
+
+ for (let p of particles) {
+ p.update(mouse.x, mouse.y);
+ p.draw(ctx);
+ }
+
+ drawLines();
+
+ for (let b of clickBurst) {
+ b.life--;
+ b.x += b.vx;
+ b.y += b.vy;
+ b.vx *= 0.98;
+ b.vy *= 0.98;
+ ctx.beginPath();
+ ctx.arc(b.x, b.y, 2 + b.life / 10, 0, Math.PI * 2);
+ ctx.fillStyle = b.color;
+ ctx.fill();
+ if (b.life <= 0) {
+ clickBurst.splice(clickBurst.indexOf(b), 1);
+ }
+ }
+
+ requestAnimationFrame(animate);
+ }
+
+ animate();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/6acc7c672157/qwen3-14b.png b/data/artifacts/6acc7c672157/qwen3-14b.png
new file mode 100644
index 0000000..50352b1
Binary files /dev/null and b/data/artifacts/6acc7c672157/qwen3-14b.png differ
diff --git a/data/artifacts/789f61c347ae/gemma3-12b.html b/data/artifacts/789f61c347ae/gemma3-12b.html
new file mode 100644
index 0000000..498b340
--- /dev/null
+++ b/data/artifacts/789f61c347ae/gemma3-12b.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Bouncing Balls</title>
+<style>
+body { margin: 0; overflow: hidden; background-color: #222; }
+#container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 400px; height: 400px; border: 1px solid white; background-color: #333;}
+#controls { position: absolute; top: 10px; left: 10px; color: white; font-family: sans-serif; }
+</style>
+</head>
+<body>
+
+<div id="container"></div>
+<div id="controls">
+ Gravity: <input type="range" id="gravitySlider" min="0.1" max="2" value="1" step="0.05"><br>
+ <button id="addButton">Add Ball</button>
+</div>
+
+<script>
+const container = document.getElementById('container');
+const gravitySlider = document.getElementById('gravitySlider');
+const addButton = document.getElementById('addButton');
+
+const ctx = container.getContext('2d');
+container.width = 400;
+container.height = 400;
+
+let balls = [];
+let gravity = 1;
+let hexagonSpeed = 0.02; // Rotation speed of the hexagon
+
+// Ball class
+class Ball {
+ constructor(x, y, radius, color) {
+ this.x = x;
+ this.y = y;
+ this.radius = radius;
+ this.color = color;
+ this.vx = (Math.random() - 0.5) * 4; // Initial X velocity
+ this.vy = (Math.random() - 0.5) * 4 + 2; // Initial Y velocity
+ this.gravity = gravity;
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ ctx.closePath();
+ }
+
+ update() {
+ this.vy += this.gravity;
+ this.x += this.vx;
+ this.y += this.vy;
+
+ // Wall collisions (rectangular container)
+ if (this.x + this.radius > container.width || this.x - this.radius < 0) {
+ this.vx = -this.vx * 0.8; // Dampen velocity
+ this.x = Math.max(this.radius, Math.min(container.width - this.radius, this.x));
+ }
+ if (this.y + this.radius > container.height || this.y - this.radius < 0) {
+ this.vy = -this.vy * 0.8; // Dampen velocity
+ this.y = Math.max(this.radius, Math.min(container.height - this.radius, this.y));
+ }
+
+ // Play sound on wall collision (simplified)
+ if (Math.abs(this.vx) < 0.1 && Math.abs(this.vy) < 0.1) { //Basic check to avoid constant noise. Could be improved.
+ playSynthTone();
+ }
+ }
+}
+
+// Add ball function
+function addBall() {
+ const radius = Math.random() * 10 + 5;
+ const x = Math.random() * (container.width - 2 * radius) + radius;
+ const y = container.height / 2; // Start from the middle top
+ const color = `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)})`;
+
+ balls.push(new Ball(x, y, radius, color));
+}
+
+//Synth tone function
+function playSynthTone() {
+ const oscillator = ctx.createOscillator();
+ oscillator.type = 'sine'; // Or 'square', 'sawtooth', 'triangle'
+ oscillator.frequency.setValueAtTime(440, ctx.currentTime); // A4 note (adjust frequency for different notes)
+ oscillator.connect(ctx.destination);
+ oscillator.start();
+ oscillator.stop(ctx.currentTime + 0.1); // Short duration
+}
+
+
+// Gravity slider event listener
+gravitySlider.addEventListener('input', () => {
+ gravity = parseFloat(gravitySlider.value);
+});
+
+// Add ball button click listener
+addButton.addEventListener('click', addBall);
+
+
+let angle = 0; //For hexagon rotation animation
+
+function animate() {
+ requestAnimationFrame(animate);
+
+ ctx.clearRect(0, 0, container.width, container.height);
+
+ angle += hexagonSpeed;
+ //Rotated canvas (simple approach - not true hexagon)
+ ctx.save();
+ ctx.translate(container.width / 2, container.height / 2);
+ ctx.rotate(angle);
+ ctx.clearRect(-container.width/2, -container.height/2, container.width, container.height); //Clear the rotated area
+
+ for (let i = 0; i < balls.length; i++) {
+ balls[i].draw();
+ balls[i].update();
+ }
+ ctx.restore();
+}
+
+
+// Initial ball creation
+for(let i=0; i<5; i++){
+ addBall();
+}
+
+animate();
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/789f61c347ae/gemma3-12b.png b/data/artifacts/789f61c347ae/gemma3-12b.png
new file mode 100644
index 0000000..00770f6
Binary files /dev/null and b/data/artifacts/789f61c347ae/gemma3-12b.png differ
diff --git a/data/artifacts/789f61c347ae/hermes3-8b.html b/data/artifacts/789f61c347ae/hermes3-8b.html
new file mode 100644
index 0000000..5d6fad9
--- /dev/null
+++ b/data/artifacts/789f61c347ae/hermes3-8b.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>Colorful Ball Game</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ }
+ canvas {
+ display: block;
+ background-color: #f0f0f0;
+ }
+ button, input[type=range] {
+ display: block;
+ margin: 10px 0;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="game"></canvas>
+ <button id="addBall">Add Ball</button>
+ <input type="range" id="gravitySlider" min="100" max="1000" value="500">
+ <script>
+ const canvas = document.getElementById('game');
+ const ctx = canvas.getContext('2d');
+
+ let balls = [];
+ let gravity = 500;
+ let containerRotation = 0;
+
+ function Ball(x, y, radius, color) {
+ this.x = x;
+ this.y = y;
+ this.radius = radius;
+ this.color = color;
+ this.speedX = 0;
+ this.speedY = 0;
+ }
+
+ Ball.prototype.draw = function() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ ctx.closePath();
+ };
+
+ Ball.prototype.update = function() {
+ this.speedY += gravity * (1 / 100000); // Simulate physics with gravity
+ this.y += this.speedY;
+
+ // Apply elastic collisions with the canvas boundaries
+ if (this.y + this.radius > canvas.height || this.y - this.radius < 0)
+ this.speedY *= -0.8; // Reduce speed by a factor of 0.8 on each bounce
+
+ this.x += this.speedX;
+
+ // Apply container rotation and elastic collisions with the walls
+ if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
+ this.speedX *= -1;
+ this.x += this.speedX; // Correct position after reversing direction
+ }
+ };
+
+ function createBall() {
+ const radius = Math.random() * 10 + 5;
+ const color = `rgb(${Math.random()*150+50}, ${Math.random()*150+50}, ${Math.random()*150+50})`;
+ let x, y;
+
+ // Make sure ball is not created inside the container
+ do {
+ x = Math.random() * (canvas.width - radius * 2) + radius;
+ y = Math.random() * (canvas.height - radius * 2) + radius;
+ } while (isInsideContainer(x, y, radius));
+
+ return new Ball(x, y, radius, color);
+ }
+
+ function isInsideContainer(x, y, radius) {
+ // Check if the ball is inside a rotating hexagon container
+ const sides = 6;
+ for(let i = 0; i < sides; i++) {
+ const angle = Math.PI * (i / sides) + containerRotation;
+ const cx = canvas.width / 2 + 50 * Math.cos(angle);
+ const cy = canvas.height / 2 + 50 * Math.sin(angle);
+
+ if (Math.abs(x - cx) <= radius && Math.abs(y - cy) <= radius)
+ return true;
+ }
+ return false;
+ }
+
+ function updateBalls() {
+ // Update each ball's position
+ balls.forEach(ball => {
+ ball.update();
+ ball.draw();
+ });
+ }
+
+ function drawContainer() {
+ const sides = 6;
+ ctx.strokeStyle = "black";
+ for(let i = 0; i <= sides; i++) {
+ const angle = Math.PI * (i / sides) + containerRotation;
+ const x1 = canvas.width / 2 + 50 * Math.cos(angle);
+ const y1 = canvas.height / 2 + 50 * Math.sin(angle);
+
+ const angleNext = Math.PI * ((i + 1) / sides) + containerRotation;
+ const x2 = canvas.width / 2 + 50 * Math.cos(angleNext);
+ const y2 = canvas.height / 2 + 50 * Math.sin(angleNext);
+
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+ }
+ }
+
+ function addBallEventListeners() {
+ document.addEventListener('keydown', event => {
+ if (['ArrowLeft', 'ArrowRight'].includes(event.key)) {
+ balls.forEach(ball => {
+ if (event.key === 'ArrowLeft')
+ ball.speedX = -3;
+ else
+ ball.speedX = 3;
+
+ ball.update();
+ ball.draw();
+ });
+
+ setTimeout(() => {
+ balls.forEach(ball => {
+ ball.speedX = 0;
+ });
+ }, 200);
+ }
+ });
+ }
+
+ function setup() {
+ canvas.width = window.innerWidth * 0.8;
+ canvas.height = window.innerHeight * 0.8;
+
+ addBallEventListeners();
+
+ document.getElementById('addBall').addEventListener('click', () => {
+ balls.push(createBall());
+ });
+
+ document.getElementById('gravitySlider').addEventListener('input', event => {
+ gravity = parseInt(event.target.value);
+ });
+ }
+
+ setup();
+
+ function gameLoop() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ updateBalls();
+ drawContainer();
+
+ requestAnimationFrame(gameLoop);
+ }
+
+ requestAnimationFrame(gameLoop);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/789f61c347ae/hermes3-8b.png b/data/artifacts/789f61c347ae/hermes3-8b.png
new file mode 100644
index 0000000..350ae01
Binary files /dev/null and b/data/artifacts/789f61c347ae/hermes3-8b.png differ
diff --git a/data/artifacts/789f61c347ae/qwen3-14b.html b/data/artifacts/789f61c347ae/qwen3-14b.html
new file mode 100644
index 0000000..daf9c9b
--- /dev/null
+++ b/data/artifacts/789f61c347ae/qwen3-14b.html
@@ -0,0 +1,208 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>Spinning Hexagon Bounce</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ width: 100%;
+ height: 100%;
+ background: #000;
+ font-family: sans-serif;
+ }
+ canvas {
+ display: block;
+ }
+ #controls {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ background: rgba(255,255,255,0.1);
+ padding: 10px;
+ border-radius: 8px;
+ color: white;
+ }
+ button {
+ background: #444;
+ color: white;
+ border: none;
+ padding: 5px 10px;
+ margin: 5px;
+ cursor: pointer;
+ }
+ input[type="range"] {
+ width: 150px;
+ }
+ </style>
+</head>
+<body>
+ <div id="controls">
+ <button id="addBall">Add Ball</button>
+ <label>Gravity: <input type="range" id="gravitySlider" min="0" max="1" step="0.01" value="0.05" /></label>
+ </div>
+ <canvas id="gameCanvas"></canvas>
+
+ <script>
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+ const controls = document.getElementById('controls');
+ const addBallBtn = document.getElementById('addBall');
+ const gravitySlider = document.getElementById('gravitySlider');
+
+ let balls = [];
+ let angle = 0;
+ let gravity = parseFloat(gravitySlider.value);
+ let isRunning = true;
+
+ const hexagonRadius = 150;
+ const center = { x: canvas.width / 2, y: canvas.height / 2 };
+
+ function resizeCanvas() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ center.x = canvas.width / 2;
+ center.y = canvas.height / 2;
+ }
+
+ window.addEventListener('resize', resizeCanvas);
+ resizeCanvas();
+
+ class Ball {
+ constructor() {
+ this.radius = 15 + Math.random() * 10;
+ this.x = center.x + Math.random() * 200 - 100;
+ this.y = center.y + Math.random() * 200 - 100;
+ this.vx = (Math.random() - 0.5) * 4;
+ this.vy = (Math.random() - 0.5) * 4;
+ this.color = `hsl(${Math.random() * 360}, 70%, 50%)`;
+ }
+
+ update() {
+ this.vy += gravity;
+ this.x += this.vx;
+ this.y += this.vy;
+
+ // Collision with hexagon walls
+ const angleStep = Math.PI / 3;
+ for (let i = 0; i < 6; i++) {
+ const theta = angle + angleStep * i;
+ const ax = center.x + hexagonRadius * Math.cos(theta);
+ const ay = center.y + hexagonRadius * Math.sin(theta);
+ const bx = center.x + hexagonRadius * Math.cos(theta + angleStep);
+ const by = center.y + hexagonRadius * Math.sin(theta + angleStep);
+
+ const normalX = ax - bx;
+ const normalY = ay - by;
+ const normal = { x: normalX, y: normalY };
+ const length = Math.sqrt(normalX * normalX + normalY * normalY);
+ normal.x /= length;
+ normal.y /= length;
+
+ const dot = (this.x - center.x) * normal.x + (this.y - center.y) * normal.y;
+ const distance = Math.sqrt((this.x - center.x) ** 2 + (this.y - center.y) ** 2);
+ if (dot > hexagonRadius - this.radius) {
+ const depth = dot - hexagonRadius + this.radius;
+ this.x -= normal.x * depth;
+ this.y -= normal.y * depth;
+
+ const velAlongNormal = this.vx * normal.x + this.vy * normal.y;
+ const restitution = 0.8;
+ const impulse = -(1 + restitution) * velAlongNormal;
+ this.vx += impulse * normal.x;
+ this.vy += impulse * normal.y;
+
+ playTone(200 + Math.random() * 500);
+ }
+ }
+
+ // Bounce off canvas edges
+ if (this.x - this.radius < 0) {
+ this.x = this.radius;
+ this.vx *= -0.8;
+ playTone(200 + Math.random() * 500);
+ } else if (this.x + this.radius > canvas.width) {
+ this.x = canvas.width - this.radius;
+ this.vx *= -0.8;
+ playTone(200 + Math.random() * 500);
+ }
+ if (this.y - this.radius < 0) {
+ this.y = this.radius;
+ this.vy *= -0.8;
+ playTone(200 + Math.random() * 500);
+ } else if (this.y + this.radius > canvas.height) {
+ this.y = canvas.height - this.radius;
+ this.vy *= -0.8;
+ playTone(200 + Math.random() * 500);
+ }
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ ctx.strokeStyle = '#000';
+ ctx.stroke();
+ }
+ }
+
+ function playTone(freq) {
+ const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ const osc = audioCtx.createOscillator();
+ const gainNode = audioCtx.createGain();
+
+ gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
+ gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.5);
+
+ osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
+ osc.type = 'sine';
+
+ osc.connect(gainNode);
+ gainNode.connect(audioCtx.destination);
+
+ osc.start();
+ osc.stop(audioCtx.currentTime + 0.5);
+ }
+
+ function animate() {
+ if (!isRunning) return;
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Draw hexagon
+ ctx.beginPath();
+ for (let i = 0; i < 6; i++) {
+ const theta = angle + i * Math.PI / 3;
+ const x = center.x + hexagonRadius * Math.cos(theta);
+ const y = center.y + hexagonRadius * Math.sin(theta);
+ ctx.lineTo(x, y);
+ }
+ ctx.closePath();
+ ctx.strokeStyle = '#0ff';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+
+ balls.forEach(ball => {
+ ball.update();
+ ball.draw();
+ });
+
+ angle += 0.01;
+ requestAnimationFrame(animate);
+ }
+
+ function addBall() {
+ balls.push(new Ball());
+ }
+
+ addBallBtn.addEventListener('click', addBall);
+ gravitySlider.addEventListener('input', () => {
+ gravity = parseFloat(gravitySlider.value);
+ });
+
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/789f61c347ae/qwen3-14b.png b/data/artifacts/789f61c347ae/qwen3-14b.png
new file mode 100644
index 0000000..a966be8
Binary files /dev/null and b/data/artifacts/789f61c347ae/qwen3-14b.png differ
diff --git a/data/artifacts/78fea53364f0/gemma3-12b.html b/data/artifacts/78fea53364f0/gemma3-12b.html
new file mode 100644
index 0000000..d2644da
--- /dev/null
+++ b/data/artifacts/78fea53364f0/gemma3-12b.html
@@ -0,0 +1,231 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Conway's Game of Life</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: #222; color: #eee; font-family: sans-serif; }
+ #controls { position: absolute; top: 10px; left: 10px; padding: 10px; background-color: rgba(34, 34, 34, 0.8); border-radius: 5px; }
+ button { margin: 5px; padding: 5px 10px; background-color: #444; color: #eee; border: none; cursor: pointer; border-radius: 3px; }
+ input[type=range] { width: 200px; margin: 5px; }
+ </style>
+</head>
+<body>
+
+<div id="controls">
+ <button id="paintBtn">Paint</button>
+ <button id="playPauseBtn">Play</button>
+ <button id="stepBtn">Step</button>
+ <input type="range" id="speedSlider" min="1" max="50" value="20">
+ <button id="randomizeBtn">Randomize</button>
+ <button id="clearBtn">Clear</button>
+ <br>
+ <button id="gliderBtn">Glider</button>
+ <button id="pulsarBtn">Pulsar</button>
+
+ <p>Generation: <span id="generationCount">0</span></p>
+</div>
+
+<canvas id="gameCanvas" width="600" height="400"></canvas>
+
+<script>
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+ const controls = document.getElementById('controls');
+
+ let gridWidth = 600;
+ let gridHeight = 400;
+ let cellSize = 10;
+ let grid = [];
+ let generationCount = 0;
+ let playing = false;
+ let paintMode = true;
+ let speed = 20; // milliseconds between generations
+
+ // Initialize the grid
+ function initGrid() {
+ grid = [];
+ for (let y = 0; y < gridHeight / cellSize; y++) {
+ grid[y] = [];
+ for (let x = 0; x < gridWidth / cellSize; x++) {
+ grid[y][x] = false; // Initially all cells are dead
+ }
+ }
+ }
+
+ // Randomly populate the grid
+ function randomizeGrid() {
+ initGrid();
+ for (let y = 0; y < gridHeight / cellSize; y++) {
+ for (let x = 0; x < gridWidth / cellSize; x++) {
+ grid[y][x] = Math.random() < 0.3; // Adjust density as needed
+ }
+ }
+ }
+
+ // Clear the grid
+ function clearGrid() {
+ initGrid();
+ }
+
+
+ // Draw the grid on the canvas
+ function drawGrid() {
+ ctx.clearRect(0, 0, gridWidth, gridHeight);
+ for (let y = 0; y < gridHeight / cellSize; y++) {
+ for (let x = 0; x < gridWidth / cellSize; x++) {
+ if (grid[y][x]) {
+ ctx.fillStyle = '#fff'; // Alive cell color
+ ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
+ } else {
+ ctx.fillStyle = '#333'; // Dead cell color
+ //ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
+ }
+ }
+ }
+ }
+
+ // Calculate the next generation
+ function calculateNextGeneration() {
+ let nextGrid = [];
+ for (let y = 0; y < gridHeight / cellSize; y++) {
+ nextGrid[y] = [];
+ for (let x = 0; x < gridWidth / cellSize; x++) {
+ let neighbors = countAliveNeighbors(x, y);
+ if (grid[y][x]) { // Cell is alive
+ nextGrid[y][x] = neighbors === 2 || neighbors === 3;
+ } else { // Cell is dead
+ nextGrid[y][x] = neighbors === 3;
+ }
+ }
+ }
+ return nextGrid;
+ }
+
+ // Count the number of alive neighbours for a given cell
+ function countAliveNeighbors(x, y) {
+ let count = 0;
+ for (let i = -1; i <= 1; i++) {
+ for (let j = -1; j <= 1; j++) {
+ if (i === 0 && j === 0) continue; // Skip the cell itself
+
+ let neighborX = x + i;
+ let neighborY = y + j;
+
+ // Wrap-around edges
+ neighborX = (neighborX + gridWidth / cellSize) % (gridWidth / cellSize);
+ neighborY = (neighborY + gridHeight / cellSize) % (gridHeight / cellSize);
+
+ if (grid[neighborY][neighborX]) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
+
+ // Update the game grid to the next generation
+ function updateGrid() {
+ grid = calculateNextGeneration();
+ generationCount++;
+ document.getElementById('generationCount').textContent = generationCount;
+ drawGrid();
+ }
+
+ // Handle mouse clicks and drags for painting
+ canvas.addEventListener('mousedown', (e) => {
+ if (paintMode) {
+ let x = Math.floor(e.offsetX / cellSize);
+ let y = Math.floor(e.offsetY / cellSize);
+ grid[y][x] = !grid[y][x]; // Toggle the cell state
+ drawGrid();
+ }
+ });
+
+ canvas.addEventListener('mousemove', (e) => {
+ if (paintMode) {
+ let x = Math.floor(e.offsetX / cellSize);
+ let y = Math.floor(e.offsetY / cellSize);
+ grid[y][x] = !grid[y][x]; // Toggle the cell state
+ drawGrid();
+ }
+ });
+
+ // Handle button clicks
+ document.getElementById('paintBtn').addEventListener('click', () => {
+ paintMode = true;
+ });
+
+ document.getElementById('playPauseBtn').addEventListener('click', () => {
+ playing = !playing;
+ if (playing) {
+ document.getElementById('playPauseBtn').textContent = 'Pause';
+ } else {
+ document.getElementById('playPauseBtn').textContent = 'Play';
+ }
+ });
+
+ document.getElementById('stepBtn').addEventListener('click', () => {
+ updateGrid();
+ });
+
+ document.getElementById('randomizeBtn').addEventListener('click', () => {
+ randomizeGrid();
+ drawGrid();
+ });
+
+ document.getElementById('clearBtn').addEventListener('click', () => {
+ clearGrid();
+ drawGrid();
+ });
+
+ // Glider Pattern
+ function glider() {
+ initGrid();
+ grid[1][2] = true;
+ grid[2][3] = true;
+ grid[3][1] = true;
+ grid[3][2] = true;
+ grid[3][3] = true;
+
+ drawGrid();
+ }
+
+
+ // Pulsar Pattern
+ function pulsar() {
+ initGrid();
+ for (let i = -3; i <= 3; i++) {
+ for (let j = -3; j <= 3; j++) {
+ if ((i === 0 && j === 0) || (Math.abs(i) === 3 && Math.abs(j) === 3)) continue;
+ grid[i + 3][j + 3] = true;
+ }
+ }
+
+ drawGrid();
+ }
+
+
+
+ document.getElementById('gliderBtn').addEventListener('click', glider);
+ document.getElementById('pulsarBtn').addEventListener('click', pulsar);
+
+
+ // Set up speed slider event listener
+ const speedSlider = document.getElementById('speedSlider');
+ speedSlider.addEventListener('input', () => {
+ speed = parseInt(speedSlider.value, 10);
+ });
+
+ initGrid();
+ drawGrid();
+
+ if (playing) {
+ setInterval(updateGrid, speed);
+ }
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/78fea53364f0/gemma3-12b.png b/data/artifacts/78fea53364f0/gemma3-12b.png
new file mode 100644
index 0000000..9bb61bf
Binary files /dev/null and b/data/artifacts/78fea53364f0/gemma3-12b.png differ
diff --git a/data/artifacts/78fea53364f0/hermes3-8b.html b/data/artifacts/78fea53364f0/hermes3-8b.html
new file mode 100644
index 0000000..318d6c1
--- /dev/null
+++ b/data/artifacts/78fea53364f0/hermes3-8b.html
@@ -0,0 +1,198 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Conway's Game of Life</title>
+ <style>
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ background-color: #f0f0f0;
+ }
+
+ canvas {
+ border: 1px solid #ccc;
+ background-color: #fff;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="lifeCanvas" width="200" height="100"></canvas>
+ <div>
+ <button id="playPause">Play/Pause</button>
+ <button id="step">Step</button>
+ <input type="range" min="0" max="10" value="0" id="speedSlider">
+ <button id="randomize">Randomize</button>
+ <button id="clear">Clear</button>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('lifeCanvas');
+ const ctx = canvas.getContext('2d');
+
+ class Cell {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ }
+ }
+
+ let cells = [];
+ let isPlaying = false;
+ let speed = 0;
+
+ // Utility functions
+ function randomCell() {
+ return new Cell(Math.floor(Math.random() * (canvas.width / 10)), Math.floor(Math.random() * (canvas.height / 10)));
+ }
+
+ function drawCells(cells) {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let cell of cells) {
+ drawCell(cell.x, cell.y);
+ }
+ }
+
+ function drawCell(x, y) {
+ const cellRadius = 5;
+ ctx.beginPath();
+ ctx.arc((x * 10) + 5, (y * 10) + 5, cellRadius, 0, 2 * Math.PI);
+ if (cells.find(c => c.x === x && c.y === y)) {
+ ctx.fillStyle = 'black';
+ } else {
+ ctx.fillStyle = 'gray';
+ }
+ ctx.fill();
+ }
+
+ function countNeighbors(cell) {
+ let neighborCount = 0;
+ for (let dx of [-1, 0, 1]) {
+ for (let dy of [-1, 0, 1]) {
+ if (dx === 0 && dy === 0) continue;
+ const neighborX = cell.x + dx;
+ const neighborY = cell.y + dy;
+ if (cells.find(c => c.x === neighborX && c.y === neighborY)) {
+ neighborCount++;
+ }
+ }
+ }
+ return neighborCount;
+ }
+
+ function nextGeneration() {
+ let newCells = [...cells];
+
+ for (let cell of cells) {
+ const aliveNeighbors = countNeighbors(cell);
+ if (aliveNeighbors < 2 || aliveNeighbors > 3) {
+ // Die
+ } else if (aliveNeighbors === 3) {
+ // New life
+ newCells.push(new Cell(cell.x, cell.y));
+ }
+ }
+
+ for (let dx of [-1, 0, 1]) {
+ for (let dy of [-1, 0, 1]) {
+ if (dx === 0 && dy === 0) continue;
+ const newX = cells.find(c => c.x === (cells[0].x + dx)) ? cells.find(c => c.x === (cells[0].x + dx)).y : null;
+ const newY = cells.find(c => c.y === (cells[0].y + dy)) ? cells.find(c => c.y === (cells[0].y + dy)).y : null;
+
+ if (!newCells.find(c => c.x === newX && c.y === newY) && newX !== null && newY !== null) {
+ newCells.push(new Cell(newX, newY));
+ }
+ }
+ }
+
+ return newCells;
+ }
+
+ function createGlider() {
+ cells = [
+ new Cell(1, 0),
+ new Cell(2, 1),
+ new Cell(0, 1),
+ new Cell(1, 1),
+ new Cell(3, 1)
+ ];
+ }
+
+ function createPulsar() {
+ // Pulsar pattern code here
+ }
+
+ // Event listeners
+ canvas.addEventListener('mousedown', (e) => {
+ const x = Math.floor(e.clientX / canvas.width);
+ const y = Math.floor(e.clientY / canvas.height);
+
+ if (!cells.find(c => c.x === x && c.y === y)) {
+ cells.push(new Cell(x, y));
+ }
+ drawCells(cells);
+ });
+
+ canvas.addEventListener('mousemove', (e) => {
+ const x = Math.floor(e.clientX / canvas.width);
+ const y = Math.floor(e.clientY / canvas.height);
+
+ drawCell(x, y);
+ });
+
+ document.getElementById('playPause').addEventListener('click', () => {
+ isPlaying ? pauseGame() : startGame();
+ });
+
+ function startGame() {
+ let intervalId = setInterval(() => {
+ if (!isPlaying) return;
+ cells = nextGeneration();
+ drawCells(cells);
+ }, 100 / speed);
+
+ document.getElementById('playPause').textContent = 'Play/Pause';
+ isPlaying = true;
+ }
+
+ function pauseGame() {
+ clearIntervalintervalId);
+ document.getElementById('playPause').textContent = 'Play/Pause';
+ isPlaying = false;
+ }
+
+ document.getElementById('step').addEventListener('click', () => {
+ cells = nextGeneration();
+ drawCells(cells);
+ });
+
+ document.getElementById('speedSlider').addEventListener('input', (e) => {
+ speed = e.target.value;
+ });
+
+ document.getElementById('randomize').addEventListener('click', () => {
+ for (let i = 0; i < Math.floor(Math.random() * 15); i++) {
+ cells.push(randomCell());
+ }
+ drawCells(cells);
+ });
+
+ document.getElementById('clear').addEventListener('click', () => {
+ cells = [];
+ drawCells(cells);
+ });
+
+ // Predefined patterns
+ document.getElementById('gliderButton').addEventListener('click', createGlider);
+ document.getElementById('pulsarButton').addEventListener('click', createPulsar);
+
+ // Initial setup
+ drawCells(cells);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/78fea53364f0/hermes3-8b.png b/data/artifacts/78fea53364f0/hermes3-8b.png
new file mode 100644
index 0000000..cee176a
Binary files /dev/null and b/data/artifacts/78fea53364f0/hermes3-8b.png differ
diff --git a/data/artifacts/a353bd10ea1b/gemma3-12b.html b/data/artifacts/a353bd10ea1b/gemma3-12b.html
new file mode 100644
index 0000000..d4c83d1
--- /dev/null
+++ b/data/artifacts/a353bd10ea1b/gemma3-12b.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Mandelbrot Explorer</title>
+<style>
+body { margin: 0; overflow: hidden; }
+canvas { display: block; }
+#controls { position: absolute; top: 10px; left: 10px; background-color: rgba(255, 255, 255, 0.7); padding: 10px; border-radius: 5px; z-index: 10; }
+#controls button { margin: 5px; padding: 5px 10px; cursor: pointer; }
+</style>
+</head>
+<body>
+<canvas id="mandelbrotCanvas"></canvas>
+
+<div id="controls">
+ <label for="iterations">Iterations:</label>
+ <input type="range" id="iterations" min="10" max="200" value="50"> <span id="iterationValue">50</span>
+ <br>
+ <button onclick="zoomIn()">Zoom In</button>
+ <button onclick="zoomOut()">Zoom Out</button>
+ <button onclick="resetView()">Reset</button>
+</div>
+
+<script>
+const canvas = document.getElementById('mandelbrotCanvas');
+const ctx = canvas.getContext('2d');
+const iterationsSlider = document.getElementById('iterations');
+const iterationValueDisplay = document.getElementById('iterationValue');
+
+let width, height;
+let centerX = -0.7;
+let centerY = 0.0;
+let zoom = 1.5;
+let maxIterations = 50; // Initial value matches slider default
+
+function init() {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ resizeCanvas();
+ drawMandelbrot();
+}
+
+function resizeCanvas() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ width = canvas.width;
+ height = canvas.height;
+ drawMandelbrot();
+}
+
+
+function mandelbrot(x, y, maxIterations) {
+ let z = 0;
+ for (let i = 0; i < maxIterations; i++) {
+ z = z * z + x + y * 1i; // Complex number multiplication and addition
+ if (Math.abs(z) > 2) {
+ return i;
+ }
+ }
+ return maxIterations;
+}
+
+function drawMandelbrot() {
+ const imageData = ctx.getImageData(0, 0, width, height);
+ const data = imageData.data;
+
+ for (let y = 0; y < height; y++) {
+ for (let x = 0; x < width; x++) {
+ const real = ((x - width / 2) / width) * zoom + centerX;
+ const imag = ((y - height / 2) / height) * zoom + centerY;
+
+ const iterations = mandelbrot(real, imag, maxIterations);
+
+ let colorIndex = Math.floor((iterations / maxIterations) * 360);
+ colorIndex = (colorIndex % 360); // Ensure within range
+
+ const hue = colorIndex;
+ const saturation = 1; // Full Saturation
+ const lightness = 0.5;
+
+ const r = Math.round((lightness + saturation * Math.cos(hue * Math.PI / 180)) * 255);
+ const g = Math.round((lightness + saturation * Math.cos((hue * Math.PI / 180) - (2 * Math.PI / 3))) * 255);
+ const b = Math.round((lightness + saturation * Math.cos((hue * Math.PI / 180) + (4 * Math.PI / 3))) * 255);
+
+ const index = (y * width + x) * 4;
+ data[index] = r;
+ data[index + 1] = g;
+ data[index + 2] = b;
+ data[index + 3] = 255; // Alpha
+ }
+ }
+
+ ctx.putImageData(imageData, 0, 0);
+}
+
+
+function zoomIn() {
+ zoom *= 2;
+ drawMandelbrot();
+}
+
+function zoomOut() {
+ zoom /= 2;
+ drawMandelbrot();
+}
+
+function resetView() {
+ centerX = -0.7;
+ centerY = 0.0;
+ zoom = 1.5;
+ maxIterations = parseInt(iterationsSlider.value);
+ drawMandelbrot();
+}
+
+
+iterationsSlider.addEventListener('input', () => {
+ maxIterations = parseInt(iterationsSlider.value);
+ iterationValueDisplay.textContent = maxIterations;
+ drawMandelbrot();
+});
+
+window.addEventListener('resize', resizeCanvas);
+
+init();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a353bd10ea1b/gemma3-12b.png b/data/artifacts/a353bd10ea1b/gemma3-12b.png
new file mode 100644
index 0000000..5a0abb9
Binary files /dev/null and b/data/artifacts/a353bd10ea1b/gemma3-12b.png differ
diff --git a/data/artifacts/a353bd10ea1b/hermes3-8b.html b/data/artifacts/a353bd10ea1b/hermes3-8b.html
new file mode 100644
index 0000000..c666aa3
--- /dev/null
+++ b/data/artifacts/a353bd10ea1b/hermes3-8b.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Mandelbrot Set Explorer</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ margin: 0;
+ padding: 20px;
+ background-color: #f0f0f0;
+ }
+
+ canvas {
+ border: 1px solid #ccc;
+ cursor: pointer;
+ }
+
+ button {
+ font-size: 16px;
+ }
+
+ input[type="range"] {
+ width: auto;
+ }
+ </style>
+</head>
+<body>
+
+<div style="display: flex; justify-content: space-between;">
+ <button onclick="zoomOut()">Zoom Out</button>
+ <input type="range" id="iterationSlider" min="1" max="1000" value="200" oninput="updateCanvas()">
+ <button onclick="zoomIn()">Zoom In</button>
+</div>
+
+<button onclick="reset()">Reset</button>
+
+<canvas id="mandelbrotCanvas"></canvas>
+
+<script>
+ const canvas = document.getElementById('mandelbrotCanvas');
+ const ctx = canvas.getContext('2d');
+
+ let width = 800;
+ let height = 600;
+ let centerX = -0.5;
+ let centerY = 0;
+ let zoom = 1;
+ let maxIterations = 200;
+
+ function reset() {
+ width = 800;
+ height = 600;
+ centerX = -0.5;
+ centerY = 0;
+ zoom = 1;
+ maxIterations = 200;
+ updateCanvas();
+ }
+
+ function updateCanvas() {
+ canvas.width = width * zoom;
+ canvas.height = height * zoom;
+
+ const scale = Math.max(width, height) / 2;
+ const zoomCenterX = centerX * scale;
+ const zoomCenterY = centerY * scale;
+
+ for (let x = -scale + zoomCenterX; x < width * zoom + zoomCenterX; x++) {
+ for (let y = -scale + zoomCenterY; y < height * zoom + zoomCenterY; y++) {
+ const c = {re: x, im: y};
+ const z = {re: 0, im: 0};
+ let i = 0;
+
+ while (i < maxIterations && (z.re ** 2 + z.im ** 2) < 4) {
+ z = {
+ re: z.re ** 2 - c.re ** 2 - 2 * c.re * z.im + centerX,
+ im: 2 * z.re * z.im - c.im ** 2 + centerY
+ };
+ i++;
+ }
+
+ const colorIndex = Math.floor(i / (maxIterations / 360));
+ ctx.fillStyle = `hsl(${colorIndex}, 100%, 50%)`;
+ ctx.fillRect(Math.floor(x / zoom), Math.floor(y / zoom), 1, 1);
+ }
+ }
+ }
+
+ function zoomIn() {
+ zoom *= 2;
+ updateCanvas();
+ }
+
+ function zoomOut() {
+ if (zoom > 0.5) {
+ zoom /= 2;
+ updateCanvas();
+ }
+ }
+
+ window.addEventListener('resize', () => {
+ const ratio = canvas.width / canvas.offsetWidth;
+
+ canvas.style.width = `${canvas.offsetWidth}px`;
+ canvas.style.height = `${canvas.offsetHeight}px`;
+
+ canvas.width *= ratio;
+ canvas.height *= ratio;
+ zoom *= ratio;
+ updateCanvas();
+ });
+
+ reset();
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a353bd10ea1b/hermes3-8b.png b/data/artifacts/a353bd10ea1b/hermes3-8b.png
new file mode 100644
index 0000000..2dfe8f6
Binary files /dev/null and b/data/artifacts/a353bd10ea1b/hermes3-8b.png differ
diff --git a/data/artifacts/a353bd10ea1b/qwen3-14b.html b/data/artifacts/a353bd10ea1b/qwen3-14b.html
new file mode 100644
index 0000000..d069517
--- /dev/null
+++ b/data/artifacts/a353bd10ea1b/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>Mandelbrot Explorer</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;
+ background: rgba(0, 0, 0, 0.5);
+ padding: 10px;
+ border-radius: 5px;
+ z-index: 10;
+ }
+ #controls input[type="range"] {
+ width: 200px;
+ }
+ #controls button {
+ margin-left: 10px;
+ padding: 5px 10px;
+ cursor: pointer;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="mandelbrot"></canvas>
+ <div id="controls">
+ <label>Iterations: <input type="range" id="iterationSlider" min="10" max="1000" value="200" /></label>
+ <button id="reset">Reset</button>
+ </div>
+ <script>
+ const canvas = document.getElementById('mandelbrot');
+ const ctx = canvas.getContext('2d');
+ const controls = document.getElementById('controls');
+ const iterationSlider = document.getElementById('iterationSlider');
+ const resetButton = document.getElementById('reset');
+
+ let width = canvas.width = window.innerWidth;
+ let height = canvas.height = window.innerHeight;
+ let scale = 2.0 / Math.min(width, height);
+ let offsetX = -0.5;
+ let offsetY = 0;
+ let maxIterations = 200;
+
+ window.addEventListener('resize', () => {
+ width = canvas.width = window.innerWidth;
+ height = canvas.height = window.innerHeight;
+ scale = 2.0 / Math.min(width, height);
+ drawMandelbrot();
+ });
+
+ function map(x, a, b, c, d) {
+ return (x - a) * (d - c) / (b - a) + c;
+ }
+
+ function mandelbrot(x, y, maxIter) {
+ let zx = 0, zy = 0;
+ let iteration = 0;
+ let x0 = x;
+ let y0 = y;
+ while (zx * zx + zy * zy < 4 && iteration < maxIter) {
+ let temp = zx * zx - zy * zy + x0;
+ zy = 2 * zx * zy + y0;
+ zx = temp;
+ iteration++;
+ }
+ return iteration;
+ }
+
+ function getColor(iter) {
+ const t = iter / maxIterations;
+ const r = Math.floor(255 * (t < 0.5 ? t * 2 : 1 - 2 * (t - 0.5)));
+ const g = Math.floor(255 * (t < 0.25 ? t * 4 : t >= 0.75 ? 2 - 4 * t : 1));
+ const b = Math.floor(255 * (t < 0.5 ? 1 - 2 * t : t < 0.75 ? 2 * (t - 0.5) : 1 - 2 * (t - 0.75)));
+ return [r, g, b];
+ }
+
+ function drawMandelbrot() {
+ const imageData = ctx.createImageData(width, height);
+ const data = imageData.data;
+ maxIterations = parseInt(iterationSlider.value);
+ for (let px = 0; px < width; px++) {
+ for (let py = 0; py < height; py++) {
+ const x = map(px, 0, width, offsetX, offsetX + scale);
+ const y = map(py, 0, height, offsetY, offsetY + scale);
+ const iter = mandelbrot(x, y, maxIterations);
+ const [r, g, b] = getColor(iter);
+ const idx = (py * width + px) * 4;
+ data[idx] = r;
+ data[idx + 1] = g;
+ data[idx + 2] = b;
+ data[idx + 3] = 255;
+ }
+ }
+ ctx.putImageData(imageData, 0, 0);
+ }
+
+ canvas.addEventListener('click', (e) => {
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+ const zoomFactor = 1.5;
+ const zoomX = map(x, 0, width, offsetX, offsetX + scale);
+ const zoomY = map(y, 0, height, offsetY, offsetY + scale);
+ scale /= zoomFactor;
+ offsetX = zoomX - (scale * width) / 2;
+ offsetY = zoomY - (scale * height) / 2;
+ drawMandelbrot();
+ });
+
+ canvas.addEventListener('wheel', (e) => {
+ e.preventDefault();
+ const zoomFactor = e.deltaY < 0 ? 1.2 : 0.8;
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+ const zoomX = map(x, 0, width, offsetX, offsetX + scale);
+ const zoomY = map(y, 0, height, offsetY, offsetY + scale);
+ scale *= zoomFactor;
+ offsetX = zoomX - (scale * width) / 2;
+ offsetY = zoomY - (scale * height) / 2;
+ drawMandelbrot();
+ });
+
+ resetButton.addEventListener('click', () => {
+ offsetX = -0.5;
+ offsetY = 0;
+ scale = 2.0 / Math.min(width, height);
+ iterationSlider.value = 200;
+ maxIterations = 200;
+ drawMandelbrot();
+ });
+
+ iterationSlider.addEventListener('input', () => {
+ maxIterations = parseInt(iterationSlider.value);
+ drawMandelbrot();
+ });
+
+ drawMandelbrot();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/a353bd10ea1b/qwen3-14b.png b/data/artifacts/a353bd10ea1b/qwen3-14b.png
new file mode 100644
index 0000000..5ce8909
Binary files /dev/null and b/data/artifacts/a353bd10ea1b/qwen3-14b.png differ
diff --git a/data/artifacts/c21d462d6163/gemma3-12b.html b/data/artifacts/c21d462d6163/gemma3-12b.html
new file mode 100644
index 0000000..147f47a
--- /dev/null
+++ b/data/artifacts/c21d462d6163/gemma3-12b.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Penrose P3 Tiling</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: #222; color: white; font-family: sans-serif; }
+ #controls { position: absolute; top: 10px; left: 10px; padding: 10px; background-color: rgba(34, 34, 34, 0.8); border-radius: 5px; }
+ canvas { display: block; }
+ </style>
+</head>
+<body>
+
+<div id="controls">
+ <label for="depthSlider">Subdivision Depth:</label>
+ <input type="range" id="depthSlider" min="2" max="8" value="4">
+ <br><br>
+ <input type="checkbox" id="colorToggle" checked> <label for="colorToggle">Color by Rhombus Type</label>
+ <br><br>
+ <button id="randomizeButton">Randomize Palette</button>
+</div>
+
+<canvas id="penroseCanvas"></canvas>
+
+<script>
+ const canvas = document.getElementById('penroseCanvas');
+ const ctx = canvas.getContext('2d');
+ const depthSlider = document.getElementById('depthSlider');
+ const colorToggle = document.getElementById('colorToggle');
+ const randomizeButton = document.getElementById('randomizeButton');
+
+ let palette = [
+ [0, 0, 0], // Black
+ [255, 0, 0], // Red
+ [0, 255, 0], // Green
+ [0, 0, 255], // Blue
+ [255, 255, 0], // Yellow
+ [255, 0, 255], // Magenta
+ [0, 255, 255] // Cyan
+ ];
+
+ let currentPalette = [...palette];
+
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ function randomColor() {
+ const randomIndex = Math.floor(Math.random() * currentPalette.length);
+ return currentPalette[randomIndex];
+ }
+
+
+ randomizeButton.addEventListener('click', () => {
+ currentPalette = palette.map(() => [
+ Math.floor(Math.random() * 256),
+ Math.floor(Math.random() * 256),
+ Math.floor(Math.random() * 256)
+ ]);
+ });
+
+
+
+ function drawRhombus(x, y, size, angle, color) {
+ ctx.beginPath();
+ const halfSize = size / 2;
+
+ const points = [
+ { x: x + Math.cos(angle) * halfSize, y: y + Math.sin(angle) * halfSize },
+ { x: x - Math.cos(angle) * halfSize, y: y - Math.sin(angle) * halfSize },
+ { x: x - Math.cos(angle) * halfSize, y: y + Math.sin(angle) * halfSize },
+ { x: x + Math.cos(angle) * halfSize, y: y - Math.sin(angle) * halfSize }
+ ];
+
+ ctx.moveTo(points[0].x, points[0].y);
+ for (let i = 1; i < 4; i++) {
+ ctx.lineTo(points[i].x, points[i].y);
+ }
+ ctx.closePath();
+
+ ctx.fillStyle = color;
+ ctx.fill();
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; // subtle outline
+ ctx.stroke();
+ }
+
+ function deflate(x, y, size, depth) {
+ if (depth === 0) {
+ drawRhombus(x, y, size, 0, randomColor());
+ drawRhombus(x, y, size, Math.PI / 3, randomColor());
+ return;
+ }
+
+ const newSize = size / 2;
+ deflate(x, y, newSize, depth - 1); // Lower left rhombus
+ deflate(x + newSize * Math.cos(Math.PI/6), y + newSize * Math.sin(Math.PI/6), newSize, depth - 1); // Upper right rhombus (angle changes)
+ }
+
+ function drawP3Tiling(startX, startY, initialSize, depth, colorByRhombusType) {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ if (colorByRhombusType) {
+ for (let i = 0; i < canvas.width; i += initialSize) {
+ for (let j = 0; j < canvas.height; j += initialSize) {
+ deflate(i, j, initialSize, depth);
+ }
+ }
+ } else {
+
+ const singleColor = randomColor();
+ for (let i = 0; i < canvas.width; i += initialSize) {
+ for (let j = 0; j < canvas.height; j += initialSize) {
+ deflate(i, j, initialSize, depth);
+ }
+ }
+
+ }
+ }
+
+
+ depthSlider.addEventListener('input', () => {
+ const depth = parseInt(depthSlider.value);
+ drawP3Tiling(canvas.width / 2 - (10 * Math.pow(2,depth-2)), canvas.height/2 - (10*Math.pow(2,depth-2)), 10 * Math.pow(2, depth-2), depth, colorToggle.checked);
+ });
+
+ colorToggle.addEventListener('change', () => {
+ drawP3Tiling(canvas.width / 2 - (10 * Math.pow(2,depthSlider.value - 2)), canvas.height/2 - (10*Math.pow(2,depthSlider.value-2)), 10 * Math.pow(2, depthSlider.value - 2), parseInt(depthSlider.value), colorToggle.checked);
+ });
+
+ const initialDepth = parseInt(depthSlider.value);
+ drawP3Tiling(canvas.width / 2 - (10*Math.pow(2,initialDepth-2)), canvas.height/2 - (10 * Math.pow(2,initialDepth-2)), 10 * Math.pow(2, initialDepth-2), initialDepth, colorToggle.checked);
+
+ window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ drawP3Tiling(canvas.width / 2 - (10*Math.pow(2,depthSlider.value-2)), canvas.height/2 - (10 * Math.pow(2,depthSlider.value-2)), 10 * Math.pow(2, depthSlider.value-2), parseInt(depthSlider.value), colorToggle.checked);
+ });
+
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c21d462d6163/gemma3-12b.png b/data/artifacts/c21d462d6163/gemma3-12b.png
new file mode 100644
index 0000000..5c91c44
Binary files /dev/null and b/data/artifacts/c21d462d6163/gemma3-12b.png differ
diff --git a/data/artifacts/c21d462d6163/hermes3-8b.html b/data/artifacts/c21d462d6163/hermes3-8b.html
new file mode 100644
index 0000000..6b6dc91
--- /dev/null
+++ b/data/artifacts/c21d462d6163/hermes3-8b.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Penrose P3 Rhombus Tiling</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ margin: 20px;
+ background-color: #f5f5f5;
+ }
+ canvas {
+ border: 1px solid black;
+ }
+ label {
+ display: block;
+ margin-bottom: 10px;
+ }
+ </style>
+</head>
+<body>
+ <h1>Penrose P3 Rhombus Tiling</h1>
+ <label for="subdivisionDepth">Subdivision Depth:</label>
+ <input type="range" id="subdivisionDepth" min="0" max="5" value="0">
+ <br>
+ <label for="colorByType">Color by Type:</label>
+ <input type="checkbox" id="colorByType" checked>
+ <br>
+ <button id="randomizePalette">Randomize Palette</button>
+ <br><br>
+ <canvas id="rhombusCanvas"></canvas>
+
+ <script>
+ const canvas = document.getElementById('rhombusCanvas');
+ const ctx = canvas.getContext('2d');
+
+ let width, height;
+ let subdivisionDepth = 0;
+ const colorByType = true;
+
+ function resizeCanvas() {
+ width = window.innerWidth * 0.8;
+ height = (window.innerHeight - 110) / Math.sqrt(3);
+ canvas.width = width;
+ canvas.height = height;
+ drawRhombus();
+ }
+
+ function randColor() {
+ return `hsl(${Math.random() * 360}, 100%, 50%)`;
+ }
+
+ function drawRhombus({ x, y, size, color }) {
+ ctx.fillStyle = color;
+ const halfSize = size / 2;
+
+ // Thin rhombus
+ ctx.beginPath();
+ ctx.moveTo(x + halfSize * Math.SQRT3 / 2, y);
+ ctx.lineTo(x + halfSize, y - halfSize);
+ ctx.lineTo(x + halfSize * Math.SQRT3 / 2, y - halfSize * Math.SQRT3);
+ ctx.lineTo(x + halfSize, y - height);
+ ctx.closePath();
+ ctx.fill();
+
+ // Fat rhombus
+ ctx.beginPath();
+ ctx.moveTo(x + halfSize * Math.SQRT3, y);
+ ctx.lineTo(x + size, y - height / 2);
+ ctx.lineTo(x + halfSize * Math.SQRT3, y - height);
+ ctx.lineTo(x, y - height / 2);
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ function drawRhombusRecursive({ x, y, size, color }) {
+ if (subdivisionDepth === 0) {
+ drawRhombus({ x, y, size, color });
+ return;
+ }
+
+ const newColor = colorByType ? randColor() : color;
+
+ drawRhombus({
+ x: x + size / 2,
+ y: y - height / 4,
+ size: size / 2,
+ color: newColor
+ });
+
+ drawRhombusRecursive({ x, y, size, color: newColor });
+ drawRhombusRecursive({ x: size / 2, y, size, color: newColor });
+ }
+
+ function init() {
+ resizeCanvas();
+
+ document.getElementById('subdivisionDepth').oninput = e => {
+ subdivisionDepth = parseInt(e.target.value);
+ canvas.innerHTML = '';
+ resizeCanvas();
+ drawRhombusRecursive({ x: width / 4, y: height / 2, size: width / 2, color: randColor() });
+ };
+
+ document.getElementById('colorByType').onchange = () => {
+ colorByType = !colorByType;
+ };
+
+ document.getElementById('randomizePalette').onclick = () => {
+ const rhombi = document.getElementsByClassName('rhombus');
+ for (const el of rhombi) {
+ el.style.fill = randColor();
+ }
+ };
+ }
+
+ window.onresize = resizeCanvas;
+ init();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c21d462d6163/hermes3-8b.png b/data/artifacts/c21d462d6163/hermes3-8b.png
new file mode 100644
index 0000000..15af766
Binary files /dev/null and b/data/artifacts/c21d462d6163/hermes3-8b.png differ
diff --git a/data/artifacts/c21d462d6163/qwen3-14b.html b/data/artifacts/c21d462d6163/qwen3-14b.html
new file mode 100644
index 0000000..0e5b0f8
--- /dev/null
+++ b/data/artifacts/c21d462d6163/qwen3-14b.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Penrose P3 Tiling</title>
+ <style>
+ body {
+ margin: 0;
+ background: #000;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ font-family: sans-serif;
+ }
+ canvas {
+ background: #111;
+ display: block;
+ }
+ #controls {
+ margin-top: 10px;
+ }
+ input[type=range] {
+ width: 200px;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="canvas" width="800" height="800"></canvas>
+ <div id="controls">
+ <label for="depth">Subdivision depth: <span id="depthValue">0</span></label>
+ <input type="range" id="depth" min="0" max="4" value="0">
+ <label><input type="checkbox" id="colorByType">Color by rhombus type</label>
+ <button id="randomize">Randomize palette</button>
+ </div>
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const depthSlider = document.getElementById('depth');
+ const depthValue = document.getElementById('depthValue');
+ const colorByTypeToggle = document.getElementById('colorByType');
+ const randomizeButton = document.getElementById('randomize');
+
+ let depth = parseInt(depthSlider.value);
+ depthValue.textContent = depth;
+ depthSlider.addEventListener('input', () => {
+ depth = parseInt(depthSlider.value);
+ depthValue.textContent = depth;
+ draw();
+ });
+
+ let colorByType = colorByTypeToggle.checked;
+ colorByTypeToggle.addEventListener('change', () => {
+ colorByType = colorByTypeToggle.checked;
+ draw();
+ });
+
+ let palette = ['#FF4136', '#FF851B', '#FFDC00', '#2ECC40', '#7FDBFF', '#B10DC9'];
+ randomizeButton.addEventListener('click', () => {
+ palette = [
+ '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0'),
+ '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0'),
+ '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0'),
+ '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0'),
+ '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0'),
+ '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0'),
+ ];
+ draw();
+ });
+
+ const canvasWidth = canvas.width;
+ const canvasHeight = canvas.height;
+
+ function draw() {
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
+ const start = new Rhombus(0, canvasHeight / 2, canvasWidth, canvasHeight / 2, 'fat');
+ drawRhombus(start, depth);
+ }
+
+ class Rhombus {
+ constructor(x1, y1, x2, y2, type) {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ this.type = type;
+ }
+
+ get center() {
+ return {
+ x: (this.x1 + this.x2) / 2,
+ y: (this.y1 + this.y2) / 2
+ };
+ }
+
+ get angle() {
+ return Math.atan2(this.y2 - this.y1, this.x2 - this.x1);
+ }
+
+ get length() {
+ return Math.hypot(this.x2 - this.x1, this.y2 - this.y1);
+ }
+
+ draw(ctx, depth) {
+ if (depth === 0) {
+ const color = colorByType ? this.type === 'fat' ? palette[0] : palette[1] : palette[Math.floor(Math.random() * palette.length)];
+ ctx.beginPath();
+ ctx.moveTo(this.x1, this.y1);
+ ctx.lineTo(this.x2, this.y2);
+ ctx.lineTo(this.x2 + Math.cos(this.angle + Math.PI / 2) * this.length, this.y2 + Math.sin(this.angle + Math.PI / 2) * this.length);
+ ctx.lineTo(this.x1 + Math.cos(this.angle + Math.PI / 2) * this.length, this.y1 + Math.sin(this.angle + Math.PI / 2) * this.length);
+ ctx.closePath();
+ ctx.fillStyle = color;
+ ctx.fill();
+ return;
+ }
+
+ const length = this.length / 2;
+ const angle = this.angle;
+ const nextAngle = angle + Math.PI / 2;
+
+ const points = [
+ { x: this.x1, y: this.y1 },
+ { x: this.x2, y: this.y2 },
+ { x: this.x2 + Math.cos(nextAngle) * length, y: this.y2 + Math.sin(nextAngle) * length },
+ { x: this.x1 + Math.cos(nextAngle) * length, y: this.y1 + Math.sin(nextAngle) * length }
+ ];
+
+ const a = new Rhombus(points[0].x, points[0].y, points[1].x, points[1].y, 'thin');
+ const b = new Rhombus(points[1].x, points[1].y, points[2].x, points[2].y, 'fat');
+ const c = new Rhombus(points[2].x, points[2].y, points[3].x, points[3].y, 'thin');
+ const d = new Rhombus(points[3].x, points[3].y, points[0].x, points[0].y, 'fat');
+
+ a.draw(ctx, depth - 1);
+ b.draw(ctx, depth - 1);
+ c.draw(ctx, depth - 1);
+ d.draw(ctx, depth - 1);
+ }
+ }
+
+ draw();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c21d462d6163/qwen3-14b.png b/data/artifacts/c21d462d6163/qwen3-14b.png
new file mode 100644
index 0000000..71d328a
Binary files /dev/null and b/data/artifacts/c21d462d6163/qwen3-14b.png differ
diff --git a/data/artifacts/c2d8bc75e725/claude-code.html b/data/artifacts/c2d8bc75e725/claude-code.html
new file mode 100644
index 0000000..bdc89b3
--- /dev/null
+++ b/data/artifacts/c2d8bc75e725/claude-code.html
@@ -0,0 +1,360 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Typing Speed Test</title>
+<style>
+ :root {
+ --bg: #0f1117;
+ --panel: #181b24;
+ --text: #e6e9f0;
+ --dim: #6b7280;
+ --green: #4ade80;
+ --red: #f87171;
+ --accent: #60a5fa;
+ --caret: #60a5fa;
+ }
+ * { box-sizing: border-box; margin: 0; padding: 0; }
+ body {
+ background: var(--bg);
+ color: var(--text);
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 24px;
+ }
+ .app {
+ width: 100%;
+ max-width: 780px;
+ }
+ h1 {
+ font-size: 1.35rem;
+ font-weight: 600;
+ letter-spacing: 0.02em;
+ margin-bottom: 4px;
+ }
+ .subtitle {
+ color: var(--dim);
+ font-size: 0.9rem;
+ margin-bottom: 22px;
+ }
+ .stats {
+ display: flex;
+ gap: 12px;
+ margin-bottom: 18px;
+ }
+ .stat {
+ flex: 1;
+ background: var(--panel);
+ border: 1px solid #262a36;
+ border-radius: 10px;
+ padding: 12px 16px;
+ text-align: center;
+ }
+ .stat .value {
+ font-size: 1.6rem;
+ font-weight: 700;
+ font-variant-numeric: tabular-nums;
+ }
+ .stat .label {
+ font-size: 0.72rem;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ color: var(--dim);
+ margin-top: 2px;
+ }
+ #wpm .value { color: var(--accent); }
+ #accuracy .value { color: var(--green); }
+ #errors .value { color: var(--red); }
+ .text-box {
+ background: var(--panel);
+ border: 1px solid #262a36;
+ border-radius: 12px;
+ padding: 26px 28px;
+ font-family: "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
+ font-size: 1.18rem;
+ line-height: 2.05;
+ letter-spacing: 0.02em;
+ cursor: text;
+ user-select: none;
+ transition: border-color 0.15s;
+ outline: none;
+ }
+ .text-box.focused { border-color: var(--accent); }
+ .text-box span { border-radius: 2px; }
+ .text-box span.correct { color: var(--green); }
+ .text-box span.incorrect {
+ color: var(--red);
+ background: rgba(248, 113, 113, 0.14);
+ text-decoration: underline;
+ text-decoration-color: rgba(248,113,113,0.6);
+ }
+ .text-box span.pending { color: var(--dim); }
+ .text-box span.current {
+ position: relative;
+ }
+ .text-box span.current::before {
+ content: "";
+ position: absolute;
+ left: -1px;
+ top: 12%;
+ height: 76%;
+ width: 2px;
+ background: var(--caret);
+ animation: blink 1s step-end infinite;
+ }
+ @keyframes blink { 50% { opacity: 0; } }
+ .hint {
+ color: var(--dim);
+ font-size: 0.85rem;
+ text-align: center;
+ margin-top: 14px;
+ min-height: 1.2em;
+ }
+ .controls {
+ display: flex;
+ justify-content: center;
+ margin-top: 16px;
+ }
+ button {
+ background: var(--accent);
+ color: #0b1020;
+ font-weight: 700;
+ font-size: 0.95rem;
+ border: none;
+ border-radius: 8px;
+ padding: 11px 26px;
+ cursor: pointer;
+ transition: transform 0.08s, filter 0.15s;
+ }
+ button:hover { filter: brightness(1.12); }
+ button:active { transform: scale(0.97); }
+ .done-banner {
+ margin-top: 16px;
+ text-align: center;
+ background: rgba(74, 222, 128, 0.1);
+ border: 1px solid rgba(74, 222, 128, 0.35);
+ color: var(--green);
+ border-radius: 10px;
+ padding: 12px;
+ font-weight: 600;
+ display: none;
+ }
+ .done-banner.show { display: block; }
+ #hidden-input {
+ position: absolute;
+ opacity: 0;
+ pointer-events: none;
+ left: -9999px;
+ }
+</style>
+</head>
+<body>
+<div class="app">
+ <h1>Typing Speed Test</h1>
+ <p class="subtitle">Type the paragraph below as fast and accurately as you can.</p>
+
+ <div class="stats">
+ <div class="stat" id="wpm"><div class="value">0</div><div class="label">WPM</div></div>
+ <div class="stat" id="accuracy"><div class="value">100%</div><div class="label">Accuracy</div></div>
+ <div class="stat" id="errors"><div class="value">0</div><div class="label">Errors</div></div>
+ <div class="stat" id="time"><div class="value">0s</div><div class="label">Time</div></div>
+ </div>
+
+ <div class="text-box" id="text-box" tabindex="0" aria-label="Typing area. Click and start typing."></div>
+ <input id="hidden-input" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" aria-hidden="true">
+
+ <div class="hint" id="hint">Click the text and start typing — the timer starts on your first key.</div>
+ <div class="done-banner" id="done-banner"></div>
+
+ <div class="controls">
+ <button id="restart">Restart with new paragraph</button>
+ </div>
+</div>
+
+<script>
+(function () {
+ var PARAGRAPHS = [
+ "The quick brown fox jumps over the lazy dog while the afternoon sun warms the quiet meadow. Practice makes progress, and every keystroke brings you closer to effortless speed and precision.",
+ "Somewhere beyond the harbor, gulls wheeled above the fishing boats as the tide slipped out. A good typist keeps a steady rhythm, letting the words flow naturally instead of rushing and stumbling.",
+ "Great software is built one small decision at a time. Clear names, short functions, and honest tests turn a tangled mess into something a stranger can read and improve without fear.",
+ "The library smelled of old paper and quiet ambition. Rows of spines in faded colors held centuries of arguments, discoveries, and stories waiting patiently for curious hands to open them.",
+ "Rain tapped gently on the tin roof as the kettle began to whistle. She wrapped her hands around the warm mug, watching clouds drift across the valley like slow gray ships.",
+ "Speed without accuracy is just noise. Focus first on hitting every character cleanly, keep your eyes on the text instead of your fingers, and the pace will rise on its own."
+ ];
+
+ var textBox = document.getElementById("text-box");
+ var hiddenInput = document.getElementById("hidden-input");
+ var wpmEl = document.querySelector("#wpm .value");
+ var accEl = document.querySelector("#accuracy .value");
+ var errEl = document.querySelector("#errors .value");
+ var timeEl = document.querySelector("#time .value");
+ var hintEl = document.getElementById("hint");
+ var doneBanner = document.getElementById("done-banner");
+ var restartBtn = document.getElementById("restart");
+
+ var target = "";
+ var pos = 0;
+ var startTime = null;
+ var finished = false;
+ var totalKeystrokes = 0;
+ var errorKeystrokes = 0;
+ var currentErrors = 0;
+ var timerId = null;
+ var lastParagraph = -1;
+ var spans = [];
+
+ function pickParagraph() {
+ var i;
+ do { i = Math.floor(Math.random() * PARAGRAPHS.length); }
+ while (PARAGRAPHS.length > 1 && i === lastParagraph);
+ lastParagraph = i;
+ return PARAGRAPHS[i];
+ }
+
+ function renderText() {
+ textBox.innerHTML = "";
+ spans = [];
+ for (var i = 0; i < target.length; i++) {
+ var s = document.createElement("span");
+ s.textContent = target[i];
+ s.className = "pending";
+ textBox.appendChild(s);
+ spans.push(s);
+ }
+ if (spans.length) spans[0].classList.add("current");
+ }
+
+ function elapsedSeconds() {
+ return startTime ? (Date.now() - startTime) / 1000 : 0;
+ }
+
+ function updateStats() {
+ var secs = elapsedSeconds();
+ var minutes = secs / 60;
+ var correctChars = pos - currentErrors;
+ var wpm = minutes > 0 ? Math.round((correctChars / 5) / minutes) : 0;
+ var acc = totalKeystrokes > 0
+ ? Math.round(((totalKeystrokes - errorKeystrokes) / totalKeystrokes) * 100)
+ : 100;
+ wpmEl.textContent = String(Math.max(0, wpm));
+ accEl.textContent = acc + "%";
+ errEl.textContent = String(errorKeystrokes);
+ timeEl.textContent = Math.floor(secs) + "s";
+ }
+
+ function startTimer() {
+ startTime = Date.now();
+ timerId = setInterval(updateStats, 250);
+ }
+
+ function stopTimer() {
+ if (timerId) { clearInterval(timerId); timerId = null; }
+ }
+
+ function finish() {
+ finished = true;
+ stopTimer();
+ updateStats();
+ hintEl.textContent = "";
+ doneBanner.textContent = "Finished! " + wpmEl.textContent + " WPM at " +
+ accEl.textContent + " accuracy in " + timeEl.textContent + ".";
+ doneBanner.classList.add("show");
+ }
+
+ function handleChar(ch) {
+ if (finished || pos >= target.length) return;
+ if (startTime === null) {
+ startTime = null; startTimer();
+ hintEl.textContent = "Timer running… keep going!";
+ }
+ totalKeystrokes++;
+ var span = spans[pos];
+ span.classList.remove("current", "pending");
+ if (ch === target[pos]) {
+ span.classList.add("correct");
+ } else {
+ span.classList.add("incorrect");
+ errorKeystrokes++;
+ currentErrors++;
+ }
+ pos++;
+ if (pos >= target.length) {
+ updateStats();
+ finish();
+ return;
+ }
+ spans[pos].classList.add("current");
+ updateStats();
+ }
+
+ function handleBackspace() {
+ if (finished || pos === 0) return;
+ spans[pos] && spans[pos].classList.remove("current");
+ pos--;
+ var span = spans[pos];
+ if (span.classList.contains("incorrect")) currentErrors--;
+ span.classList.remove("correct", "incorrect");
+ span.classList.add("pending", "current");
+ updateStats();
+ }
+
+ function onKeyDown(e) {
+ if (e.metaKey || e.ctrlKey || e.altKey) return;
+ if (e.key === "Backspace") {
+ e.preventDefault();
+ handleBackspace();
+ } else if (e.key === "Tab") {
+ e.preventDefault();
+ } else if (e.key.length === 1) {
+ e.preventDefault();
+ handleChar(e.key);
+ }
+ }
+
+ function reset() {
+ stopTimer();
+ target = pickParagraph();
+ pos = 0;
+ startTime = null;
+ finished = false;
+ totalKeystrokes = 0;
+ errorKeystrokes = 0;
+ currentErrors = 0;
+ renderText();
+ wpmEl.textContent = "0";
+ accEl.textContent = "100%";
+ errEl.textContent = "0";
+ timeEl.textContent = "0s";
+ doneBanner.classList.remove("show");
+ hintEl.textContent = "Click the text and start typing — the timer starts on your first key.";
+ focusArea();
+ }
+
+ function focusArea() {
+ hiddenInput.value = "";
+ textBox.classList.add("focused");
+ try { hiddenInput.focus({ preventScroll: true }); } catch (e) { hiddenInput.focus(); }
+ }
+
+ textBox.addEventListener("mousedown", function (e) { e.preventDefault(); focusArea(); });
+ textBox.addEventListener("keydown", onKeyDown);
+ hiddenInput.addEventListener("keydown", onKeyDown);
+ hiddenInput.addEventListener("blur", function () {
+ if (document.activeElement !== textBox) textBox.classList.remove("focused");
+ });
+ textBox.addEventListener("focus", function () { textBox.classList.add("focused"); });
+ textBox.addEventListener("blur", function () {
+ if (document.activeElement !== hiddenInput) textBox.classList.remove("focused");
+ });
+
+ restartBtn.addEventListener("click", function () { reset(); });
+
+ reset();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c2d8bc75e725/claude-code.png b/data/artifacts/c2d8bc75e725/claude-code.png
new file mode 100644
index 0000000..d0b2d3c
Binary files /dev/null and b/data/artifacts/c2d8bc75e725/claude-code.png differ
diff --git a/data/artifacts/c2d8bc75e725/gemma3-12b.html b/data/artifacts/c2d8bc75e725/gemma3-12b.html
new file mode 100644
index 0000000..1c8dfb5
--- /dev/null
+++ b/data/artifacts/c2d8bc75e725/gemma3-12b.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Typing Speed Test</title>
+ <style>
+ body { font-family: sans-serif; }
+ #text { white-space: pre; word-wrap: break-word; margin-bottom: 20px; }
+ #input { width: 80%; padding: 10px; font-size: 16px; border: 1px solid #ccc; resize: none; }
+ #stats { margin-top: 20px; font-weight: bold; }
+ .correct { color: green; }
+ .incorrect { color: red; }
+ #restart { display: block; margin-top: 20px; padding: 10px 20px; background-color: #4CAF50; color: white; border: none; cursor: pointer; }
+ </style>
+</head>
+<body>
+ <h1>Typing Speed Test</h1>
+
+ <p id="text">This is a sample paragraph of text for the typing speed test. It contains several sentences to provide a reasonable amount of content. The goal is to type as quickly and accurately as possible. Let's see how fast you can type!</p>
+
+ <textarea id="input" rows="4"></textarea>
+
+ <div id="stats">
+ WPM: <span id="wpm">0</span> | Accuracy: <span id="accuracy">0%</span>
+ </div>
+
+ <button id="restart">Restart Test</button>
+
+
+ <script>
+ const textElement = document.getElementById('text');
+ const inputElement = document.getElementById('input');
+ const wpmElement = document.getElementById('wpm');
+ const accuracyElement = document.getElementById('accuracy');
+ const restartButton = document.getElementById('restart');
+
+ let startTime;
+ let charIndex = 0;
+ let typedText = '';
+ let wpm = 0;
+ let accuracy = 100;
+
+
+ function getRandomParagraph() {
+ const paragraphs = [
+ "This is a sample paragraph of text for the typing speed test. It contains several sentences to provide a reasonable amount of content. The goal is to type as quickly and accurately as possible. Let's see how fast you can type!",
+ "The quick brown fox jumps over the lazy dog. This is a classic pangram, meaning it contains every letter of the alphabet.",
+ "Coding is fun! It allows you to create things that solve problems or entertain people. Learning to code takes practice and patience.",
+ "Artificial intelligence is rapidly changing the world around us. From self-driving cars to virtual assistants, AI is becoming increasingly integrated into our daily lives."
+ ];
+ const randomIndex = Math.floor(Math.random() * paragraphs.length);
+ return paragraphs[randomIndex];
+ }
+
+
+
+ function updateStats() {
+ const typedTextLength = typedText.length;
+ const originalTextLength = textElement.textContent.length;
+
+ if (typedTextLength > 0) {
+ wpm = (typedTextLength / 5) * 60 / startTime; // Assuming average word length of 5 characters, converting seconds to minutes
+ accuracy = Math.round((typedTextLength - incorrectChars) / typedTextLength * 100);
+
+ } else {
+ wpm = 0;
+ accuracy = 0;
+ }
+
+
+ wpmElement.textContent = wpm.toFixed(2);
+ accuracyElement.textContent = accuracy + '%';
+ }
+
+
+
+ function highlightChars() {
+ const originalText = textElement.textContent.replace(/\s/g, ''); // Remove whitespace for accurate comparison
+ const typedTextNoWhitespace = typedText.replace(/\s/g, '');
+
+ const chars = inputElement.children;
+
+ for (let i = 0; i < chars.length; i++) {
+ if (i < originalTextLength) {
+ if (originalText[i] === typedTextNoWhitespace[i]) {
+ chars[i].classList.remove('incorrect');
+ chars[i].classList.add('correct');
+ } else {
+ chars[i].classList.remove('correct');
+ chars[i].classList.add('incorrect');
+ }
+ } else {
+ chars[i].classList.remove('correct', 'incorrect'); // Clear highlighting for extra chars
+ }
+ }
+ }
+
+
+ function createCharElements(text) {
+ inputElement.innerHTML = '';
+ for (let i = 0; i < text.length; i++) {
+ const span = document.createElement('span');
+ span.textContent = text[i];
+ inputElement.appendChild(span);
+ }
+ }
+
+ function startTest() {
+ startTime = Date.now();
+ textElement.textContent = getRandomParagraph();
+ typedText = '';
+ charIndex = 0;
+ wpm = 0;
+ accuracy = 100;
+ inputElement.value = '';
+ createCharElements(textElement.textContent);
+
+ highlightChars(); // Initial highlight after restart
+ updateStats();
+
+
+ }
+
+ inputElement.addEventListener('input', () => {
+ typedText = inputElement.value;
+ let incorrectChars = 0;
+ const originalText = textElement.textContent.replace(/\s/g, '');
+ const typedTextNoWhitespace = typedText.replace(/\s/g, '');
+
+
+ if (typedTextNoWhitespace.length > charIndex) {
+
+ for (let i = 0; i < typedTextNoWhitespace.length && i < originalTextLength; i++) {
+ if(originalText[i] !== typedTextNoWhitespace[i]){
+ incorrectChars++;
+ }
+
+ }
+
+
+ }
+
+
+
+ updateStats();
+ highlightChars();
+ });
+
+
+
+
+ restartButton.addEventListener('click', startTest);
+
+ startTest();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c2d8bc75e725/gemma3-12b.png b/data/artifacts/c2d8bc75e725/gemma3-12b.png
new file mode 100644
index 0000000..ea715a5
Binary files /dev/null and b/data/artifacts/c2d8bc75e725/gemma3-12b.png differ
diff --git a/data/artifacts/c2d8bc75e725/gpt.html b/data/artifacts/c2d8bc75e725/gpt.html
new file mode 100644
index 0000000..1bc8d59
--- /dev/null
+++ b/data/artifacts/c2d8bc75e725/gpt.html
@@ -0,0 +1,387 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Typing Speed Test</title>
+ <style>
+ * {
+ box-sizing: border-box;
+ }
+ body {
+ margin: 0;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ background: #0f172a;
+ color: #e5e7eb;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 100vh;
+ }
+ .app {
+ background: #020617;
+ padding: 20px 22px 24px;
+ border-radius: 14px;
+ box-shadow: 0 18px 45px rgba(0,0,0,0.6);
+ max-width: 800px;
+ width: 100%;
+ border: 1px solid #1e293b;
+ }
+ h1 {
+ margin: 0 0 10px;
+ font-size: 22px;
+ color: #f9fafb;
+ text-align: center;
+ }
+ .top-row {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ margin-bottom: 14px;
+ }
+ .metric-group {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+ .metric {
+ background: #020617;
+ border-radius: 10px;
+ border: 1px solid #1f2937;
+ padding: 6px 10px;
+ min-width: 90px;
+ }
+ .metric-label {
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.07em;
+ color: #9ca3af;
+ margin-bottom: 2px;
+ }
+ .metric-value {
+ font-size: 18px;
+ font-variant-numeric: tabular-nums;
+ }
+ .metric-unit {
+ font-size: 11px;
+ color: #6b7280;
+ margin-left: 3px;
+ }
+ #restartBtn {
+ background: #2563eb;
+ color: #eef2ff;
+ border: none;
+ border-radius: 999px;
+ padding: 8px 16px;
+ font-size: 13px;
+ font-weight: 500;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ box-shadow: 0 8px 20px rgba(37,99,235,0.35);
+ transition: background 0.12s, transform 0.08s, box-shadow 0.12s;
+ white-space: nowrap;
+ }
+ #restartBtn:hover {
+ background: #1d4ed8;
+ box-shadow: 0 10px 24px rgba(37,99,235,0.45);
+ transform: translateY(-0.5px);
+ }
+ #restartBtn:active {
+ transform: translateY(0.5px) scale(0.99);
+ box-shadow: 0 6px 16px rgba(37,99,235,0.3);
+ }
+ #restartBtn span.icon {
+ font-size: 14px;
+ line-height: 1;
+ }
+ .sample-container {
+ background: #020617;
+ border-radius: 12px;
+ padding: 14px 12px;
+ border: 1px solid #1f2937;
+ margin-bottom: 12px;
+ max-height: 200px;
+ overflow-y: auto;
+ scrollbar-width: thin;
+ scrollbar-color: #4b5563 #020617;
+ }
+ .sample-container::-webkit-scrollbar {
+ width: 6px;
+ }
+ .sample-container::-webkit-scrollbar-track {
+ background: #020617;
+ }
+ .sample-container::-webkit-scrollbar-thumb {
+ background: #4b5563;
+ border-radius: 999px;
+ }
+ #sampleText {
+ font-size: 16px;
+ line-height: 1.6;
+ color: #9ca3af;
+ word-wrap: break-word;
+ font-family: "SF Mono", ui-monospace, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ }
+ #sampleText span {
+ white-space: pre-wrap;
+ padding: 0 0.5px;
+ border-radius: 2px;
+ }
+ #sampleText span.current {
+ position: relative;
+ background: rgba(37,99,235,0.16);
+ }
+ #sampleText span.current::after {
+ content: "";
+ position: absolute;
+ right: -1px;
+ top: 1px;
+ bottom: 1px;
+ width: 2px;
+ background: #3b82f6;
+ animation: caretBlink 1s step-start infinite;
+ }
+ @keyframes caretBlink {
+ 50% { opacity: 0; }
+ }
+ #sampleText span.correct {
+ color: #bbf7d0;
+ }
+ #sampleText span.incorrect {
+ color: #fecaca;
+ background: rgba(248,113,113,0.08);
+ }
+ #sampleText span.extra {
+ color: #fca5a5;
+ background: rgba(220,38,38,0.18);
+ text-decoration: line-through;
+ }
+ .input-wrapper {
+ position: relative;
+ }
+ #inputArea {
+ width: 100%;
+ background: #020617;
+ border-radius: 10px;
+ border: 1px solid #1f2937;
+ padding: 10px 12px;
+ resize: none;
+ min-height: 80px;
+ max-height: 120px;
+ color: #e5e7eb;
+ font-size: 15px;
+ line-height: 1.5;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ outline: none;
+ overflow-y: auto;
+ }
+ #inputArea:focus {
+ border-color: #2563eb;
+ box-shadow: 0 0 0 1px rgba(37,99,235,0.4);
+ }
+ #inputArea::placeholder {
+ color: #4b5563;
+ }
+ .hint {
+ margin-top: 6px;
+ font-size: 11px;
+ color: #6b7280;
+ display: flex;
+ justify-content: space-between;
+ gap: 10px;
+ flex-wrap: wrap;
+ }
+ .hint span.key {
+ border-radius: 4px;
+ border: 1px solid #4b5563;
+ padding: 1px 5px;
+ font-size: 10px;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: #d1d5db;
+ }
+ @media (max-width: 640px) {
+ .app {
+ border-radius: 0;
+ height: 100vh;
+ max-width: 100%;
+ }
+ body {
+ align-items: stretch;
+ }
+ }
+ </style>
+</head>
+<body>
+ <div class="app">
+ <h1>Typing Speed Test</h1>
+ <div class="top-row">
+ <div class="metric-group">
+ <div class="metric">
+ <div class="metric-label">Speed</div>
+ <div class="metric-value"><span id="wpm">0</span><span class="metric-unit">WPM</span></div>
+ </div>
+ <div class="metric">
+ <div class="metric-label">Accuracy</div>
+ <div class="metric-value"><span id="accuracy">100</span><span class="metric-unit">%</span></div>
+ </div>
+ <div class="metric">
+ <div class="metric-label">Time</div>
+ <div class="metric-value"><span id="time">0.0</span><span class="metric-unit">s</span></div>
+ </div>
+ </div>
+ <button id="restartBtn"><span class="icon">⟳</span><span>New Paragraph</span></button>
+ </div>
+ <div class="sample-container">
+ <div id="sampleText"></div>
+ </div>
+ <div class="input-wrapper">
+ <textarea id="inputArea" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" placeholder="Start typing here…"></textarea>
+ </div>
+ <div class="hint">
+ <span>Timer starts with your first keystroke.</span>
+ <span><span class="key">Tab</span> to focus • <span class="key">Esc</span> to restart</span>
+ </div>
+ </div>
+
+ <script>
+ (function () {
+ const paragraphs = [
+ "The quick brown fox jumps over the lazy dog. This classic pangram contains every letter of the alphabet and is often used to test fonts or typing speed.",
+ "Typing is a skill that improves with consistent practice. Focus on accuracy first, and speed will naturally increase over time as your fingers learn the patterns.",
+ "In a quiet room, the only sound was the soft tapping of keys. Each word formed a small step forward, turning thoughts into lines of text on the glowing screen.",
+ "Developing muscle memory allows you to type without looking at the keyboard. Your hands begin to glide from key to key, guided more by rhythm than by sight.",
+ "Short bursts of focused practice are more effective than long, distracted sessions. Set a simple goal, like three accurate paragraphs, and work steadily toward it."
+ ];
+
+ const sampleTextEl = document.getElementById("sampleText");
+ const inputArea = document.getElementById("inputArea");
+ const wpmEl = document.getElementById("wpm");
+ const accuracyEl = document.getElementById("accuracy");
+ const timeEl = document.getElementById("time");
+ const restartBtn = document.getElementById("restartBtn");
+
+ let targetText = "";
+ let startTime = null;
+ let timerInterval = null;
+ let totalKeystrokes = 0;
+ let errorCount = 0;
+ let finished = false;
+
+ function chooseParagraph() {
+ return paragraphs[Math.floor(Math.random() * paragraphs.length)];
+ }
+
+ function renderSample() {
+ sampleTextEl.innerHTML = "";
+ for (let i = 0; i < targetText.length; i++) {
+ const span = document.createElement("span");
+ span.textContent = targetText[i];
+ if (i === 0) span.classList.add("current");
+ sampleTextEl.appendChild(span);
+ }
+ }
+
+ function resetTest() {
+ clearInterval(timerInterval);
+ timerInterval = null;
+ startTime = null;
+ totalKeystrokes = 0;
+ errorCount = 0;
+ finished = false;
+ wpmEl.textContent = "0";
+ accuracyEl.textContent = "100";
+ timeEl.textContent = "0.0";
+ targetText = chooseParagraph();
+ renderSample();
+ inputArea.value = "";
+ inputArea.focus();
+ }
+
+ function updateMetrics() {
+ if (!startTime) return;
+ const now = performance.now();
+ const elapsedMs = now - startTime;
+ const elapsedMinutes = elapsedMs / 60000;
+ const elapsedSeconds = elapsedMs / 1000;
+ timeEl.textContent = elapsedSeconds.toFixed(1);
+
+ const typedLength = inputArea.value.length;
+ if (elapsedMinutes > 0 && typedLength > 0) {
+ const wpm = (typedLength / 5) / elapsedMinutes;
+ wpmEl.textContent = Math.max(0, Math.round(wpm)).toString();
+ } else {
+ wpmEl.textContent = "0";
+ }
+
+ const correctKeystrokes = Math.max(0, totalKeystrokes - errorCount);
+ const accuracy = totalKeystrokes > 0 ? (correctKeystrokes / totalKeystrokes) * 100 : 100;
+ accuracyEl.textContent = accuracy.toFixed(0);
+ }
+
+ function handleInput() {
+ const typed = inputArea.value;
+ if (!startTime && typed.length > 0) {
+ startTime = performance.now();
+ timerInterval = setInterval(updateMetrics, 100);
+ }
+
+ const spans = sampleTextEl.querySelectorAll("span");
+ totalKeystrokes = typed.length;
+ errorCount = 0;
+
+ const minLen = Math.min(typed.length, targetText.length);
+ for (let i = 0; i < spans.length; i++) {
+ spans[i].className = "";
+ }
+
+ for (let i = 0; i < minLen; i++) {
+ if (typed[i] === targetText[i]) {
+ spans[i].classList.add("correct");
+ } else {
+ spans[i].classList.add("incorrect");
+ errorCount++;
+ }
+ }
+
+ if (typed.length < targetText.length) {
+ if (!finished) {
+ spans[typed.length].classList.add("current");
+ }
+ }
+
+ if (typed.length > targetText.length) {
+ // mark extra chars virtually (no span exists); count as errors
+ errorCount += typed.length - targetText.length;
+ }
+
+ if (!finished && typed === targetText) {
+ finished = true;
+ clearInterval(timerInterval);
+ timerInterval = null;
+ updateMetrics();
+ } else {
+ updateMetrics();
+ }
+ }
+
+ restartBtn.addEventListener("click", resetTest);
+ inputArea.addEventListener("input", handleInput);
+
+ document.addEventListener("keydown", (e) => {
+ if (e.key === "Escape") {
+ e.preventDefault();
+ resetTest();
+ } else if (e.key === "Tab") {
+ inputArea.focus();
+ }
+ });
+
+ resetTest();
+ })();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c2d8bc75e725/gpt.png b/data/artifacts/c2d8bc75e725/gpt.png
new file mode 100644
index 0000000..5ca1da0
Binary files /dev/null and b/data/artifacts/c2d8bc75e725/gpt.png differ
diff --git a/data/artifacts/c2d8bc75e725/grok.html b/data/artifacts/c2d8bc75e725/grok.html
new file mode 100644
index 0000000..29a820c
--- /dev/null
+++ b/data/artifacts/c2d8bc75e725/grok.html
@@ -0,0 +1,331 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Typing Speed Test</title>
+<style>
+ :root {
+ --bg: #0f1419;
+ --surface: #1a2332;
+ --border: #2d3a4f;
+ --text: #e7ecf3;
+ --muted: #8b9cb3;
+ --green: #3dd68c;
+ --red: #f07178;
+ --accent: #5b9fd4;
+ --caret: #e6c07b;
+ }
+ * { box-sizing: border-box; margin: 0; padding: 0; }
+ body {
+ font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
+ background: var(--bg);
+ color: var(--text);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 24px;
+ line-height: 1.5;
+ }
+ .container {
+ width: 100%;
+ max-width: 720px;
+ }
+ h1 {
+ font-size: 1.5rem;
+ font-weight: 600;
+ letter-spacing: -0.02em;
+ margin-bottom: 8px;
+ text-align: center;
+ }
+ .subtitle {
+ color: var(--muted);
+ font-size: 0.9rem;
+ text-align: center;
+ margin-bottom: 28px;
+ }
+ .stats {
+ display: flex;
+ gap: 16px;
+ margin-bottom: 20px;
+ justify-content: center;
+ }
+ .stat {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 14px 28px;
+ text-align: center;
+ min-width: 120px;
+ }
+ .stat-value {
+ font-size: 1.75rem;
+ font-weight: 700;
+ font-variant-numeric: tabular-nums;
+ color: var(--accent);
+ }
+ .stat-label {
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--muted);
+ margin-top: 2px;
+ }
+ .text-display {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 28px;
+ font-size: 1.2rem;
+ line-height: 1.85;
+ letter-spacing: 0.01em;
+ margin-bottom: 16px;
+ min-height: 160px;
+ word-wrap: break-word;
+ user-select: none;
+ cursor: text;
+ }
+ .text-display .char {
+ border-radius: 2px;
+ transition: background 0.05s;
+ }
+ .text-display .correct { color: var(--green); }
+ .text-display .incorrect {
+ color: var(--red);
+ background: rgba(240, 113, 120, 0.15);
+ text-decoration: underline;
+ }
+ .text-display .current {
+ background: rgba(230, 192, 123, 0.35);
+ border-left: 2px solid var(--caret);
+ margin-left: -2px;
+ }
+ .text-display .pending { color: var(--muted); }
+ .input-wrap {
+ position: relative;
+ margin-bottom: 20px;
+ }
+ #typing-input {
+ width: 100%;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 16px 20px;
+ font-size: 1.05rem;
+ color: var(--text);
+ font-family: inherit;
+ outline: none;
+ resize: none;
+ transition: border-color 0.15s;
+ }
+ #typing-input:focus { border-color: var(--accent); }
+ #typing-input:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+ .actions {
+ display: flex;
+ justify-content: center;
+ gap: 12px;
+ }
+ button {
+ background: var(--accent);
+ color: #0f1419;
+ border: none;
+ border-radius: 10px;
+ padding: 12px 28px;
+ font-size: 0.95rem;
+ font-weight: 600;
+ cursor: pointer;
+ font-family: inherit;
+ transition: transform 0.1s, opacity 0.15s;
+ }
+ button:hover { opacity: 0.9; }
+ button:active { transform: scale(0.97); }
+ .done-msg {
+ text-align: center;
+ margin-top: 16px;
+ color: var(--green);
+ font-weight: 600;
+ font-size: 1.05rem;
+ min-height: 1.5em;
+ }
+ .hint {
+ text-align: center;
+ color: var(--muted);
+ font-size: 0.8rem;
+ margin-top: 12px;
+ }
+</style>
+</head>
+<body>
+ <div class="container">
+ <h1>Typing Speed Test</h1>
+ <p class="subtitle">Type the text below as quickly and accurately as you can</p>
+ <div class="stats">
+ <div class="stat">
+ <div class="stat-value" id="wpm">0</div>
+ <div class="stat-label">WPM</div>
+ </div>
+ <div class="stat">
+ <div class="stat-value" id="accuracy">100%</div>
+ <div class="stat-label">Accuracy</div>
+ </div>
+ <div class="stat">
+ <div class="stat-value" id="time">0s</div>
+ <div class="stat-label">Time</div>
+ </div>
+ </div>
+ <div class="text-display" id="text-display" aria-live="polite"></div>
+ <div class="input-wrap">
+ <textarea id="typing-input" rows="3" placeholder="Start typing here..." autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
+ </div>
+ <div class="actions">
+ <button type="button" id="restart-btn">Restart</button>
+ </div>
+ <div class="done-msg" id="done-msg"></div>
+ <p class="hint">Click the text or input area and begin typing. Timer starts on first keystroke.</p>
+ </div>
+<script>
+(function () {
+ const PARAGRAPHS = [
+ "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs. How vexingly quick daft zebras jump when they see a bright yellow kite flying high above the meadow.",
+ "Technology has transformed the way we communicate, work, and live our daily lives. From smartphones to artificial intelligence, innovation continues to shape our future in ways we never imagined possible.",
+ "Beneath the starlit sky, a gentle breeze whispered through the ancient trees. The river flowed quietly beside the old stone bridge, carrying secrets of forgotten seasons toward the distant sea.",
+ "Success is not final, failure is not fatal: it is the courage to continue that counts. Every expert was once a beginner who refused to give up when the path ahead seemed uncertain and long.",
+ "The library smelled of old paper and polished wood. Sunlight filtered through tall windows, illuminating dust motes that danced above rows of leather-bound volumes waiting to share their stories.",
+ "Cooking is both an art and a science. Measuring ingredients carefully matters, yet intuition and taste guide the final touches that turn a simple recipe into something truly memorable and delicious.",
+ "Across the vast desert, dunes shifted slowly under the relentless sun. Travelers followed faint trails marked by weathered stones, hoping to reach the oasis before nightfall brought cold winds.",
+ "Music has the power to evoke emotions that words alone cannot express. A single melody can transport you to another time, unlock forgotten memories, and connect strangers across cultures and languages."
+ ];
+
+ const display = document.getElementById("text-display");
+ const input = document.getElementById("typing-input");
+ const wpmEl = document.getElementById("wpm");
+ const accEl = document.getElementById("accuracy");
+ const timeEl = document.getElementById("time");
+ const doneMsg = document.getElementById("done-msg");
+ const restartBtn = document.getElementById("restart-btn");
+
+ let target = "";
+ let startTime = null;
+ let timerId = null;
+ let finished = false;
+
+ function pickParagraph() {
+ let next;
+ do {
+ next = PARAGRAPHS[Math.floor(Math.random() * PARAGRAPHS.length)];
+ } while (next === target && PARAGRAPHS.length > 1);
+ return next;
+ }
+
+ function renderText(typed) {
+ const frag = document.createDocumentFragment();
+ for (let i = 0; i < target.length; i++) {
+ const span = document.createElement("span");
+ span.className = "char";
+ span.textContent = target[i];
+ if (i < typed.length) {
+ span.classList.add(typed[i] === target[i] ? "correct" : "incorrect");
+ } else if (i === typed.length && !finished) {
+ span.classList.add("current");
+ } else {
+ span.classList.add("pending");
+ }
+ frag.appendChild(span);
+ }
+ display.innerHTML = "";
+ display.appendChild(frag);
+ }
+
+ function calcStats(typed) {
+ let correct = 0;
+ const len = typed.length;
+ for (let i = 0; i < len; i++) {
+ if (typed[i] === target[i]) correct++;
+ }
+ const accuracy = len === 0 ? 100 : Math.round((correct / len) * 100);
+ let wpm = 0;
+ let seconds = 0;
+ if (startTime) {
+ seconds = (Date.now() - startTime) / 1000;
+ const minutes = seconds / 60;
+ if (minutes > 0) {
+ wpm = Math.round((correct / 5) / minutes);
+ }
+ }
+ return { wpm, accuracy, seconds: Math.floor(seconds), correct };
+ }
+
+ function updateUI() {
+ const typed = input.value;
+ renderText(typed);
+ const { wpm, accuracy, seconds } = calcStats(typed);
+ wpmEl.textContent = wpm;
+ accEl.textContent = accuracy + "%";
+ timeEl.textContent = seconds + "s";
+
+ if (typed.length >= target.length && !finished) {
+ finished = true;
+ input.disabled = true;
+ if (timerId) {
+ clearInterval(timerId);
+ timerId = null;
+ }
+ const final = calcStats(typed);
+ doneMsg.textContent =
+ "Complete! " + final.wpm + " WPM with " + final.accuracy + "% accuracy in " + final.seconds + "s.";
+ }
+ }
+
+ function onInput() {
+ if (finished) return;
+ if (!startTime && input.value.length > 0) {
+ startTime = Date.now();
+ timerId = setInterval(updateUI, 200);
+ }
+ if (input.value.length > target.length) {
+ input.value = input.value.slice(0, target.length);
+ }
+ updateUI();
+ }
+
+ function restart() {
+ if (timerId) {
+ clearInterval(timerId);
+ timerId = null;
+ }
+ target = pickParagraph();
+ startTime = null;
+ finished = false;
+ input.disabled = false;
+ input.value = "";
+ doneMsg.textContent = "";
+ wpmEl.textContent = "0";
+ accEl.textContent = "100%";
+ timeEl.textContent = "0s";
+ renderText("");
+ input.focus();
+ }
+
+ display.addEventListener("click", function () {
+ input.focus();
+ });
+ input.addEventListener("input", onInput);
+ restartBtn.addEventListener("click", restart);
+
+ input.addEventListener("keydown", function (e) {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ if (finished) restart();
+ }
+ });
+
+ restart();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c2d8bc75e725/grok.png b/data/artifacts/c2d8bc75e725/grok.png
new file mode 100644
index 0000000..93ffcfc
Binary files /dev/null and b/data/artifacts/c2d8bc75e725/grok.png differ
diff --git a/data/artifacts/c2d8bc75e725/hermes3-8b.html b/data/artifacts/c2d8bc75e725/hermes3-8b.html
new file mode 100644
index 0000000..3b1f792
--- /dev/null
+++ b/data/artifacts/c2d8bc75e725/hermes3-8b.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Typing Speed Test</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ line-height: 1.6;
+ margin: 20px;
+ }
+
+ .text-container {
+ border: 1px solid #ccc;
+ padding: 10px;
+ max-width: 600px;
+ margin: 0 auto;
+ }
+
+ textarea {
+ width: 100%;
+ min-height: 200px;
+ }
+
+ .result-container {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 20px;
+ }
+
+ .result {
+ font-weight: bold;
+ }
+
+ .highlight-green {
+ background-color: #dff0f7;
+ }
+
+ .highlight-red {
+ background-color: #ffcccc;
+ }
+ </style>
+</head>
+<body>
+ <div class="text-container">
+ <p id="paragraph">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla in cursus quam, velit. Fusce sollicitudin sapien sit amet lacus fringilla, ut aliquam justo sodales. Sed vel congue ex. In hac habitasse platea dictumst. Quisque auctor vestibulum odio, euismod facilisis ligula feugiat at. Curabitur non nulla mauris. Proin venenatis tellus id nisi porttitor, in interdum sapien fringilla.</p>
+ <textarea id="user-input" oninput="checkInput()"></textarea>
+ <div class="result-container">
+ <span>Words per minute: <span id="wpm">0</span></span>
+ <span>Accuracy: <span id="accuracy">0</span>%</span>
+ </div>
+ <button onclick="restartTest()">Restart</button>
+ </div>
+
+ <script>
+ let userText = '';
+ let startTime;
+ let totalChars = 0;
+ let correctChars = 0;
+
+ function checkInput() {
+ const userInput = document.getElementById('user-input').value;
+
+ if (userInput === userText) {
+ // User has not typed anything new
+ return;
+ }
+
+ const currentTime = Date.now();
+ const timeElapsed = currentTime - startTime;
+
+ if (timeElapsed > 10000) { // Check if the test duration exceeds 10 seconds
+ calculateResult(userInput, true);
+ } else {
+ calculateResult(userInput, false);
+ }
+ }
+
+ function calculateResult(userInput, timeout) {
+ const comparisonText = document.getElementById('paragraph').innerText;
+
+ let charsEntered = 0;
+ let correctCharsEntered = 0;
+
+ for (let i = 0; i < userInput.length && i < comparisonText.length; i++) {
+ if (userInput[i] === comparisonText[i]) {
+ document.getElementById('user-input').setSelectionRange(i - correctCharsEntered, i);
+ document.getElementById('user-input').classList.remove('highlight-red');
+ document.getElementById('user-input').classList.add('highlight-green');
+ correctCharsEntered++;
+ } else {
+ if (!timeout) { // If not a timeout calculation
+ document.getElementById('user-input').setSelectionRange(i - correctCharsEntered, i);
+ }
+ document.getElementById('user-input').classList.remove('highlight-green');
+ document.getElementById('user-input').classList.add('highlight-red');
+ }
+
+ charsEntered++;
+ }
+
+ if (charsEntered > 0) {
+ totalChars += charsEntered;
+ correctChars += correctCharsEntered;
+ }
+
+ const accuracy = ((correctChars * 100) / totalChars).toFixed(2);
+ document.getElementById('wpm').textContent = Math.floor(charsEntered / (timeElapsed / 1000) * 60);
+ document.getElementById('accuracy').textContent = accuracy;
+
+ if (timeout) {
+ document.getElementById('user-input').classList.remove('highlight-green');
+ document.getElementById('user-input').classList.add('highlight-red');
+ }
+
+ userText = userInput;
+ startTime = Date.now();
+ }
+
+ function restartTest() {
+ const paragraph = document.getElementById('paragraph');
+ const userInput = document.getElementById('user-input');
+
+ userInput.value = '';
+ totalChars = 0;
+ correctChars = 0;
+
+ userInput.classList.remove('highlight-green');
+ userInput.classList.remove('highlight-red');
+
+ userInput.focus();
+
+ paragraph.innerHTML = `${paragraph.innerText}
+ <span style="color: gray;">
+ (Click Restart to try again)
+ </span>`;
+ }
+
+ document.getElementById('user-input').addEventListener('input', () => {
+ if (!startTime) { // Start timer only once
+ startTime = Date.now();
+ }
+ });
+
+ restartTest(); // Initialize with a fresh paragraph
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c2d8bc75e725/hermes3-8b.png b/data/artifacts/c2d8bc75e725/hermes3-8b.png
new file mode 100644
index 0000000..985173d
Binary files /dev/null and b/data/artifacts/c2d8bc75e725/hermes3-8b.png differ
diff --git a/data/artifacts/c2d8bc75e725/hf-qwen-coder-32b.html b/data/artifacts/c2d8bc75e725/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..ca2cbde
--- /dev/null
+++ b/data/artifacts/c2d8bc75e725/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>Typing Speed Test</title>
+ <style>
+ body { font-family: Arial, sans-serif; margin: 20px; }
+ #text { white-space: pre-wrap; line-height: 1.5; }
+ .correct { color: green; }
+ .incorrect { color: red; }
+ input { width: 100%; padding: 10px; font-size: 16px; margin-top: 20px; box-sizing: border-box; }
+ #stats { margin-top: 20px; }
+ </style>
+</head>
+<body>
+ <div id="text"></div>
+ <input type="text" id="input" autofocus onkeydown="handleInput(event)" />
+ <div id="stats">WPM: <span id="wpm">0</span> | Accuracy: <span id="accuracy">100%</span></div>
+ <button onclick="restart()">Restart</button>
+ <script>
+ const paragraphs = [
+ "The quick brown fox jumps over the lazy dog.",
+ "A journey of a thousand miles begins with a single step.",
+ "To be or not to be, that is the question."
+ ];
+
+ let text = "";
+ let inputElement = document.getElementById("input");
+ let wpmElement = document.getElementById("wpm");
+ let accuracyElement = document.getElementById("accuracy");
+ let startTime;
+ let correctChars = 0;
+ let totalChars = 0;
+
+ function displayText() {
+ text = paragraphs[Math.floor(Math.random() * paragraphs.length)];
+ document.getElementById("text").innerText = text;
+ inputElement.value = "";
+ inputElement.focus();
+ correctChars = 0;
+ totalChars = 0;
+ startTime = new Date().getTime();
+ updateStats();
+ }
+
+ function handleInput(event) {
+ let typedText = inputElement.value;
+ if (typedText.length === 1 && startTime === undefined) {
+ startTime = new Date().getTime();
+ }
+ for (let i = 0; i < text.length; i++) {
+ let span = document.createElement("span");
+ span.textContent = text[i];
+ if (i < typedText.length) {
+ totalChars++;
+ if (text[i] === typedText[i]) {
+ correctChars++;
+ span.className = "correct";
+ } else {
+ span.className = "incorrect";
+ }
+ }
+ document.getElementById("text").replaceChild(span, document.getElementById("text").childNodes[i]);
+ }
+ updateStats();
+ }
+
+ function updateStats() {
+ let elapsedTime = (new Date().getTime() - startTime) / 1000;
+ if (elapsedTime > 0) {
+ wpmElement.textContent = Math.round((correctChars / 5) / (elapsedTime / 60));
+ }
+ accuracyElement.textContent = totalChars === 0 ? "100%" : Math.round((correctChars / totalChars) * 100) + "%";
+ }
+
+ function restart() {
+ displayText();
+ startTime = undefined;
+ }
+
+ displayText();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c2d8bc75e725/hf-qwen-coder-32b.png b/data/artifacts/c2d8bc75e725/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..6ca10aa
Binary files /dev/null and b/data/artifacts/c2d8bc75e725/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/c2d8bc75e725/kimi.html b/data/artifacts/c2d8bc75e725/kimi.html
new file mode 100644
index 0000000..18504b1
--- /dev/null
+++ b/data/artifacts/c2d8bc75e725/kimi.html
@@ -0,0 +1,259 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Typing Speed Test</title>
+<style>
+* { box-sizing: border-box; margin: 0; padding: 0; }
+body {
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 20px;
+}
+.container {
+ background: white;
+ border-radius: 16px;
+ padding: 2.5rem;
+ width: 100%;
+ max-width: 800px;
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
+}
+h1 {
+ text-align: center;
+ color: #2d3748;
+ margin-bottom: 1.5rem;
+ font-size: 2rem;
+}
+.stats {
+ display: flex;
+ justify-content: space-around;
+ margin-bottom: 2rem;
+ gap: 1rem;
+}
+.stat-box {
+ text-align: center;
+ flex: 1;
+ padding: 1rem;
+ background: #f7fafc;
+ border-radius: 12px;
+ border: 2px solid #e2e8f0;
+}
+.stat-label {
+ font-size: 0.875rem;
+ color: #718096;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin-bottom: 0.25rem;
+}
+.stat-value {
+ font-size: 2rem;
+ font-weight: 700;
+ color: #2d3748;
+}
+#text-display {
+ font-size: 1.25rem;
+ line-height: 1.8;
+ color: #4a5568;
+ margin-bottom: 1.5rem;
+ padding: 1.5rem;
+ background: #f7fafc;
+ border-radius: 12px;
+ min-height: 120px;
+ letter-spacing: 0.5px;
+}
+#text-display span {
+ border-radius: 3px;
+ transition: all 0.1s;
+ padding: 2px 1px;
+}
+.correct {
+ color: #059669;
+ background: #d1fae5;
+}
+.incorrect {
+ color: #dc2626;
+ background: #fee2e2;
+ text-decoration: underline;
+}
+#input-field {
+ width: 100%;
+ padding: 1rem;
+ font-size: 1.125rem;
+ border: 2px solid #e2e8f0;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+ transition: border-color 0.2s;
+ color: #2d3748;
+}
+#input-field:focus {
+ outline: none;
+ border-color: #667eea;
+}
+#restart-btn {
+ width: 100%;
+ padding: 1rem;
+ background: #667eea;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ font-size: 1.125rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: transform 0.1s, background 0.2s;
+}
+#restart-btn:hover {
+ background: #5568d3;
+ transform: translateY(-1px);
+}
+#restart-btn:active {
+ transform: translateY(0);
+}
+.completed {
+ opacity: 0.7;
+ pointer-events: none;
+}
+</style>
+</head>
+<body>
+<div class="container">
+ <h1>Typing Speed Test</h1>
+ <div class="stats">
+ <div class="stat-box">
+ <div class="stat-label">WPM</div>
+ <div class="stat-value" id="wpm">0</div>
+ </div>
+ <div class="stat-box">
+ <div class="stat-label">Accuracy</div>
+ <div class="stat-value" id="accuracy">100%</div>
+ </div>
+ <div class="stat-box">
+ <div class="stat-label">Time</div>
+ <div class="stat-value" id="time">0s</div>
+ </div>
+ </div>
+ <div id="text-display"></div>
+ <input type="text" id="input-field" placeholder="Start typing here..." autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
+ <button id="restart-btn">Restart Test</button>
+</div>
+
+<script>
+const paragraphs = [
+ "The quick brown fox jumps over the lazy dog. This sentence contains every letter of the alphabet and serves as an excellent benchmark for typing proficiency and keyboard familiarity.",
+ "Technology is best when it brings people together. It allows us to connect across vast distances and share ideas instantaneously. The future of human communication depends on our ability to adapt to these tools.",
+ "In the middle of difficulty lies opportunity. Challenges are what make life interesting and overcoming them is what makes life meaningful. Never give up on something you truly want to achieve.",
+ "Success is not final, failure is not fatal: it is the courage to continue that counts. The only way to do great work is to love what you do. Believe you can and you are halfway there.",
+ "Nature does not hurry, yet everything is accomplished. The earth has music for those who listen. Look deep into nature, and then you will understand everything better."
+];
+
+let currentText = '';
+let startTime = null;
+let timerInterval = null;
+let isFinished = false;
+
+const textDisplay = document.getElementById('text-display');
+const inputField = document.getElementById('input-field');
+const wpmDisplay = document.getElementById('wpm');
+const accuracyDisplay = document.getElementById('accuracy');
+const timeDisplay = document.getElementById('time');
+const restartBtn = document.getElementById('restart-btn');
+
+function init() {
+ currentText = paragraphs[Math.floor(Math.random() * paragraphs.length)];
+ startTime = null;
+ isFinished = false;
+ inputField.value = '';
+ inputField.disabled = false;
+ inputField.classList.remove('completed');
+ inputField.focus();
+ clearInterval(timerInterval);
+
+ renderText();
+ updateStats(0, 100, 0);
+}
+
+function renderText() {
+ textDisplay.innerHTML = '';
+ currentText.split('').forEach(char => {
+ const span = document.createElement('span');
+ span.textContent = char;
+ textDisplay.appendChild(span);
+ });
+}
+
+function updateStats(wpm, accuracy, seconds) {
+ wpmDisplay.textContent = wpm;
+ accuracyDisplay.textContent = accuracy + '%';
+ timeDisplay.textContent = seconds + 's';
+}
+
+function calculateStats(input, correct) {
+ if (!startTime) return { wpm: 0, accuracy: 100, seconds: 0 };
+
+ const elapsed = (Date.now() - startTime) / 1000;
+ const minutes = elapsed / 60;
+ const wpm = minutes > 0 ? Math.round((input.length / 5) / minutes) : 0;
+ const accuracy = input.length > 0 ? Math.round((correct / input.length) * 100) : 100;
+
+ return { wpm, accuracy, seconds: Math.round(elapsed) };
+}
+
+inputField.addEventListener('input', () => {
+ if (isFinished) return;
+
+ const value = inputField.value;
+
+ if (!startTime && value.length > 0) {
+ startTime = Date.now();
+ timerInterval = setInterval(() => {
+ const currentVal = inputField.value;
+ let correct = 0;
+ for (let i = 0; i < currentVal.length; i++) {
+ if (currentVal[i] === currentText[i]) correct++;
+ }
+ const stats = calculateStats(currentVal, correct);
+ updateStats(stats.wpm, stats.accuracy, stats.seconds);
+ }, 1000);
+ }
+
+ const spans = textDisplay.querySelectorAll('span');
+ let correctCount = 0;
+
+ spans.forEach((span, idx) => {
+ const char = value[idx];
+ if (char === undefined) {
+ span.className = '';
+ } else if (char === currentText[idx]) {
+ span.className = 'correct';
+ correctCount++;
+ } else {
+ span.className = 'incorrect';
+ }
+ });
+
+ const stats = calculateStats(value, correctCount);
+ updateStats(stats.wpm, stats.accuracy, stats.seconds);
+
+ if (value.length >= currentText.length) {
+ isFinished = true;
+ clearInterval(timerInterval);
+ inputField.disabled = true;
+ inputField.classList.add('completed');
+ updateStats(stats.wpm, stats.accuracy, stats.seconds);
+ }
+});
+
+restartBtn.addEventListener('click', init);
+
+textDisplay.addEventListener('click', () => {
+ if (!isFinished) inputField.focus();
+});
+
+init();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c2d8bc75e725/kimi.png b/data/artifacts/c2d8bc75e725/kimi.png
new file mode 100644
index 0000000..209d214
Binary files /dev/null and b/data/artifacts/c2d8bc75e725/kimi.png differ
diff --git a/data/artifacts/c2d8bc75e725/qwen25-7b.html b/data/artifacts/c2d8bc75e725/qwen25-7b.html
new file mode 100644
index 0000000..aa1ebf9
--- /dev/null
+++ b/data/artifacts/c2d8bc75e725/qwen25-7b.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Typing Speed Test</title>
+ <style>
+ body { font-family: Arial, sans-serif; text-align: center; }
+ #container { width: 50%; margin: auto; }
+ .result { margin-top: 20px; display: none; }
+ .highlight-green { background-color: lightgreen; }
+ .highlight-red { background-color: salmon; }
+ </style>
+</head>
+<body>
+ <div id="container">
+ <h1>Typing Speed Test</h1>
+ <textarea id="inputText" placeholder="Type here..." rows="5"></textarea><br>
+ <p id="sampleText">The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.</p>
+ <div class="result">
+ <span>WPM: </span><span id="wpmResult">0</span>
+ <span> Accuracy: </span><span id="accuracyResult">0%</span>
+ <button onclick="restart()">Restart</button>
+ </div>
+ </div>
+
+ <script>
+ const sampleText = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.";
+ let inputText = document.getElementById('inputText');
+ let sampleParagraph = document.getElementById('sampleText').innerText;
+ let wpmResult = document.getElementById('wpmResult');
+ let accuracyResult = document.getElementById('accuracyResult');
+
+ function highlight() {
+ const userText = inputText.value;
+ for (let i = 0; i < Math.min(sampleParagraph.length, userText.length); i++) {
+ if (sampleParagraph[i] === userText[i]) {
+ document.querySelectorAll(`[data-index="${i}"]`).forEach(el => el.classList.add('highlight-green'));
+ } else {
+ document.querySelectorAll(`[data-index="${i}"]`).forEach(el => el.classList.add('highlight-red'));
+ }
+ }
+ }
+
+ inputText.addEventListener('input', () => {
+ highlight();
+ });
+
+ function restart() {
+ inputText.value = '';
+ sampleParagraph = document.getElementById('sampleText').innerText;
+ wpmResult.innerText = '0';
+ accuracyResult.innerText = '0%';
+ document.querySelectorAll('.highlight-green, .highlight-red').forEach(el => el.classList.remove('highlight-green', 'highlight-red'));
+ }
+
+ restart();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c2d8bc75e725/qwen25-7b.png b/data/artifacts/c2d8bc75e725/qwen25-7b.png
new file mode 100644
index 0000000..9d91093
Binary files /dev/null and b/data/artifacts/c2d8bc75e725/qwen25-7b.png differ
diff --git a/data/artifacts/c2d8bc75e725/qwen3-14b.html b/data/artifacts/c2d8bc75e725/qwen3-14b.html
new file mode 100644
index 0000000..b1d7bb4
--- /dev/null
+++ b/data/artifacts/c2d8bc75e725/qwen3-14b.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Typing Speed Test</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ background: #f4f4f4;
+ color: #333;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px;
+ }
+ #container {
+ background: #fff;
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+ width: 100%;
+ max-width: 600px;
+ text-align: left;
+ }
+ #sample-text {
+ font-size: 18px;
+ line-height: 1.6;
+ margin-bottom: 20px;
+ }
+ #sample-text span {
+ display: inline-block;
+ min-width: 1ch;
+ }
+ #input-area {
+ font-size: 18px;
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+ border: 2px solid #ccc;
+ border-radius: 4px;
+ outline: none;
+ }
+ #stats {
+ margin-top: 15px;
+ font-size: 16px;
+ color: #555;
+ }
+ #restart-btn {
+ margin-top: 20px;
+ padding: 10px 20px;
+ font-size: 16px;
+ background: #007bff;
+ color: #fff;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ }
+ #restart-btn:hover {
+ background: #0056b3;
+ }
+ </style>
+</head>
+<body>
+ <div id="container">
+ <div id="sample-text"></div>
+ <textarea id="input-area" placeholder="Start typing here..."></textarea>
+ <div id="stats">WPM: 0 | Accuracy: 0%</div>
+ <button id="restart-btn">Restart</button>
+ </div>
+
+ <script>
+ const sampleParagraphs = [
+ "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
+ "Programming is fun, but typing is harder than it looks. Keep practicing and you'll improve over time.",
+ "A journey of a thousand miles begins with a single step. Don't give up, no matter how difficult it seems.",
+ "The early bird catches the worm, but the second mouse gets the cheese. Be wise and choose your path carefully."
+ ];
+
+ const sampleText = document.getElementById("sample-text");
+ const inputArea = document.getElementById("input-area");
+ const stats = document.getElementById("stats");
+ const restartBtn = document.getElementById("restart-btn");
+
+ let startTime;
+ let correctChars = 0;
+ let errors = 0;
+ let typedChars = 0;
+
+ function updateStats() {
+ const timeElapsed = (Date.now() - startTime) / 60000; // in minutes
+ const wordsPerMinute = Math.round((typedChars / 5) / timeElapsed);
+ const accuracy = Math.round((correctChars / (correctChars + errors)) * 100);
+ stats.textContent = `WPM: ${wordsPerMinute} | Accuracy: ${accuracy}%`;
+ }
+
+ function startTest() {
+ const text = sampleParagraphs[Math.floor(Math.random() * sampleParagraphs.length)];
+ sampleText.innerHTML = '';
+ const textArray = text.split('').map(char => `<span>${char}</span>`);
+ sampleText.innerHTML = textArray.join('');
+ inputArea.value = '';
+ inputArea.disabled = false;
+ inputArea.focus();
+ startTime = Date.now();
+ correctChars = 0;
+ errors = 0;
+ typedChars = 0;
+ stats.textContent = "WPM: 0 | Accuracy: 0%";
+ }
+
+ function highlightChars() {
+ const input = inputArea.value;
+ const sampleSpans = sampleText.querySelectorAll("span");
+ const inputChars = input.split('');
+ const minLen = Math.min(inputChars.length, sampleSpans.length);
+
+ for (let i = 0; i < minLen; i++) {
+ const span = sampleSpans[i];
+ const inputChar = inputChars[i];
+ const sampleChar = span.textContent;
+
+ if (inputChar === sampleChar) {
+ span.style.color = "green";
+ correctChars++;
+ } else {
+ span.style.color = "red";
+ errors++;
+ }
+ }
+
+ for (let i = minLen; i < sampleSpans.length; i++) {
+ sampleSpans[i].style.color = "inherit";
+ }
+ }
+
+ inputArea.addEventListener("input", () => {
+ typedChars = inputArea.value.length;
+ highlightChars();
+ updateStats();
+ });
+
+ restartBtn.addEventListener("click", startTest);
+
+ startTest();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/c2d8bc75e725/qwen3-14b.png b/data/artifacts/c2d8bc75e725/qwen3-14b.png
new file mode 100644
index 0000000..dea6a6c
Binary files /dev/null and b/data/artifacts/c2d8bc75e725/qwen3-14b.png differ
diff --git a/data/artifacts/edbe5de71e26/claude-code.html b/data/artifacts/edbe5de71e26/claude-code.html
new file mode 100644
index 0000000..c824731
--- /dev/null
+++ b/data/artifacts/edbe5de71e26/claude-code.html
@@ -0,0 +1,245 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>Ink Fluid</title>
+<style>
+ html,body{margin:0;height:100%;overflow:hidden;background:#07070c;font-family:system-ui,-apple-system,Segoe UI,sans-serif}
+ #c{display:block;width:100vw;height:100vh;cursor:crosshair;touch-action:none}
+ #ui{position:fixed;top:14px;left:14px;display:flex;gap:10px;align-items:center;
+ background:rgba(18,18,28,.65);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
+ padding:9px 12px;border-radius:12px;border:1px solid rgba(255,255,255,.09);
+ box-shadow:0 4px 24px rgba(0,0,0,.45);user-select:none}
+ #ui label{color:#9aa3b2;font-size:12px;letter-spacing:.04em}
+ #color{width:34px;height:26px;border:none;border-radius:6px;background:none;padding:0;cursor:pointer}
+ #clear{background:#232336;color:#dfe3ea;border:1px solid rgba(255,255,255,.12);
+ border-radius:8px;padding:5px 14px;font-size:13px;cursor:pointer;transition:background .15s}
+ #clear:hover{background:#32324c}
+ #hint{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);color:#6b7280;
+ font-size:13px;letter-spacing:.06em;pointer-events:none;transition:opacity 1s}
+</style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<div id="ui">
+ <label for="color">Ink</label>
+ <input type="color" id="color" value="#41c8ff">
+ <button id="clear">Clear</button>
+</div>
+<div id="hint">drag to pour ink</div>
+<script>
+(function(){
+"use strict";
+// ---------- grid-based fluid solver (Stam-style stable fluids) ----------
+const N = 128, S = N + 2, SZ = S * S;
+const IX = (i, j) => i + S * j;
+const u = new Float32Array(SZ), v = new Float32Array(SZ);
+const u0 = new Float32Array(SZ), v0 = new Float32Array(SZ);
+const R = new Float32Array(SZ), G = new Float32Array(SZ), B = new Float32Array(SZ);
+const T = new Float32Array(SZ); // scratch for dye diffusion/advection
+const curl = new Float32Array(SZ);
+const dt = 1 / 60, DYE_DIFF = 0.00012, DISSIPATE = 0.994, VORT = 12;
+
+function setBnd(b, x){
+ for(let i = 1; i <= N; i++){
+ x[IX(0, i)] = b === 1 ? -x[IX(1, i)] : x[IX(1, i)];
+ x[IX(N+1, i)] = b === 1 ? -x[IX(N, i)] : x[IX(N, i)];
+ x[IX(i, 0)] = b === 2 ? -x[IX(i, 1)] : x[IX(i, 1)];
+ x[IX(i, N+1)] = b === 2 ? -x[IX(i, N)] : x[IX(i, N)];
+ }
+ x[IX(0,0)] = 0.5 * (x[IX(1,0)] + x[IX(0,1)]);
+ x[IX(0,N+1)] = 0.5 * (x[IX(1,N+1)] + x[IX(0,N)]);
+ x[IX(N+1,0)] = 0.5 * (x[IX(N,0)] + x[IX(N+1,1)]);
+ x[IX(N+1,N+1)] = 0.5 * (x[IX(N,N+1)] + x[IX(N+1,N)]);
+}
+function linSolve(b, x, x0, a, c, iter){
+ const inv = 1 / c;
+ for(let k = 0; k < iter; k++){
+ for(let j = 1; j <= N; j++){
+ let id = IX(1, j);
+ for(let i = 1; i <= N; i++, id++){
+ x[id] = (x0[id] + a * (x[id-1] + x[id+1] + x[id-S] + x[id+S])) * inv;
+ }
+ }
+ setBnd(b, x);
+ }
+}
+function diffuse(b, x, x0, diff, iter){
+ const a = dt * diff * N * N;
+ linSolve(b, x, x0, a, 1 + 4 * a, iter);
+}
+function advect(b, d, d0, uu, vv){
+ const dt0 = dt * N;
+ for(let j = 1; j <= N; j++){
+ for(let i = 1; i <= N; i++){
+ const id = IX(i, j);
+ let x = i - dt0 * uu[id], y = j - dt0 * vv[id];
+ if(x < 0.5) x = 0.5; if(x > N + 0.5) x = N + 0.5;
+ if(y < 0.5) y = 0.5; if(y > N + 0.5) y = N + 0.5;
+ const i0 = x | 0, j0 = y | 0, i1 = i0 + 1, j1 = j0 + 1;
+ const s1 = x - i0, s0 = 1 - s1, t1 = y - j0, t0 = 1 - t1;
+ d[id] = s0 * (t0 * d0[IX(i0,j0)] + t1 * d0[IX(i0,j1)])
+ + s1 * (t0 * d0[IX(i1,j0)] + t1 * d0[IX(i1,j1)]);
+ }
+ }
+ setBnd(b, d);
+}
+function project(uu, vv, p, div){
+ const h = 1 / N;
+ for(let j = 1; j <= N; j++){
+ for(let i = 1; i <= N; i++){
+ const id = IX(i, j);
+ div[id] = -0.5 * h * (uu[id+1] - uu[id-1] + vv[id+S] - vv[id-S]);
+ p[id] = 0;
+ }
+ }
+ setBnd(0, div); setBnd(0, p);
+ linSolve(0, p, div, 1, 4, 18);
+ for(let j = 1; j <= N; j++){
+ for(let i = 1; i <= N; i++){
+ const id = IX(i, j);
+ uu[id] -= 0.5 * (p[id+1] - p[id-1]) / h;
+ vv[id] -= 0.5 * (p[id+S] - p[id-S]) / h;
+ }
+ }
+ setBnd(1, uu); setBnd(2, vv);
+}
+function confine(){ // vorticity confinement -> lively swirls
+ for(let j = 1; j <= N; j++)
+ for(let i = 1; i <= N; i++){
+ const id = IX(i, j);
+ curl[id] = 0.5 * (v[id+1] - v[id-1] - u[id+S] + u[id-S]);
+ }
+ for(let j = 2; j < N; j++)
+ for(let i = 2; i < N; i++){
+ const id = IX(i, j);
+ let gx = Math.abs(curl[id+1]) - Math.abs(curl[id-1]);
+ let gy = Math.abs(curl[id+S]) - Math.abs(curl[id-S]);
+ const len = Math.sqrt(gx * gx + gy * gy) + 1e-5;
+ gx /= len; gy /= len;
+ u[id] += VORT * dt * gy * curl[id];
+ v[id] -= VORT * dt * gx * curl[id];
+ }
+}
+function step(){
+ confine();
+ u0.set(u); v0.set(v);
+ advect(1, u, u0, u0, v0);
+ advect(2, v, v0, u0, v0);
+ project(u, v, u0, v0);
+ for(const D of [R, G, B]){
+ T.set(D);
+ diffuse(0, T, D, DYE_DIFF, 4);
+ advect(0, D, T, u, v);
+ }
+ for(let k = 0; k < SZ; k++){ R[k] *= DISSIPATE; G[k] *= DISSIPATE; B[k] *= DISSIPATE; }
+}
+
+// ---------- ink injection ----------
+let ink = [0.25, 0.78, 1.0];
+function hexToRgb(h){
+ return [parseInt(h.slice(1,3),16)/255, parseInt(h.slice(3,5),16)/255, parseInt(h.slice(5,7),16)/255];
+}
+function splat(cx, cy, fx, fy, amt){
+ const rad = 3;
+ for(let j = -rad; j <= rad; j++)
+ for(let i = -rad; i <= rad; i++){
+ const x = Math.round(cx) + i, y = Math.round(cy) + j;
+ if(x < 1 || x > N || y < 1 || y > N) continue;
+ const w = Math.exp(-(i*i + j*j) / (rad * 1.1));
+ const id = IX(x, y);
+ u[id] += fx * w; v[id] += fy * w;
+ R[id] += ink[0] * amt * w; G[id] += ink[1] * amt * w; B[id] += ink[2] * amt * w;
+ }
+}
+
+// ---------- canvas / render ----------
+const canvas = document.getElementById('c'), ctx = canvas.getContext('2d');
+const sim = document.createElement('canvas'); sim.width = sim.height = N;
+const sctx = sim.getContext('2d');
+const img = sctx.createImageData(N, N);
+function resize(){ canvas.width = innerWidth; canvas.height = innerHeight; }
+addEventListener('resize', resize); resize();
+
+function render(){
+ const px = img.data;
+ let p = 0;
+ for(let j = 1; j <= N; j++){
+ for(let i = 1; i <= N; i++){
+ const id = IX(i, j);
+ let r = R[id] * 255, g = G[id] * 255, b = B[id] * 255;
+ px[p++] = r > 255 ? 255 : r;
+ px[p++] = g > 255 ? 255 : g;
+ px[p++] = b > 255 ? 255 : b;
+ px[p++] = 255;
+ }
+ }
+ sctx.putImageData(img, 0, 0);
+ ctx.imageSmoothingEnabled = true;
+ ctx.fillStyle = '#07070c';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.drawImage(sim, 0, 0, canvas.width, canvas.height);
+ ctx.globalCompositeOperation = 'source-over';
+}
+
+// ---------- input ----------
+let down = false, px = 0, py = 0;
+const hint = document.getElementById('hint');
+function toGrid(e){
+ const r = canvas.getBoundingClientRect();
+ return [ (e.clientX - r.left) / r.width * N + 0.5,
+ (e.clientY - r.top) / r.height * N + 0.5 ];
+}
+canvas.addEventListener('pointerdown', e => {
+ down = true; canvas.setPointerCapture(e.pointerId);
+ [px, py] = toGrid(e);
+ splat(px, py, 0, 0, 0.9);
+ hint.style.opacity = 0;
+});
+canvas.addEventListener('pointermove', e => {
+ if(!down) return;
+ const [gx, gy] = toGrid(e);
+ let dx = gx - px, dy = gy - py;
+ const speed = Math.sqrt(dx*dx + dy*dy);
+ if(speed > 0){
+ const steps = Math.max(1, Math.ceil(speed)); // interpolate fast strokes
+ const sw = Math.sin(performance.now() * 0.004) * 0.55; // oscillating side-swirl
+ for(let s = 1; s <= steps; s++){
+ const t = s / steps;
+ splat(px + dx*t, py + dy*t,
+ (dx + -dy * sw) * 2.2 / steps * 6,
+ (dy + dx * sw) * 2.2 / steps * 6,
+ Math.min(0.5, 0.12 + speed * 0.05) / steps * 2);
+ }
+ }
+ px = gx; py = gy;
+});
+addEventListener('pointerup', () => down = false);
+addEventListener('pointercancel', () => down = false);
+
+document.getElementById('color').addEventListener('input', e => ink = hexToRgb(e.target.value));
+document.getElementById('clear').addEventListener('click', () => {
+ u.fill(0); v.fill(0); R.fill(0); G.fill(0); B.fill(0);
+});
+
+// welcome burst so the canvas isn't empty on load
+(function(){
+ const cx = N * 0.5, cy = N * 0.55;
+ for(let k = 0; k < 5; k++){
+ const a = k / 5 * Math.PI * 2;
+ splat(cx + Math.cos(a) * 10, cy + Math.sin(a) * 10,
+ -Math.sin(a) * 9, Math.cos(a) * 9, 0.5);
+ }
+})();
+
+(function loop(){
+ step();
+ render();
+ requestAnimationFrame(loop);
+})();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/edbe5de71e26/claude-code.png b/data/artifacts/edbe5de71e26/claude-code.png
new file mode 100644
index 0000000..0a4519e
Binary files /dev/null and b/data/artifacts/edbe5de71e26/claude-code.png differ
diff --git a/data/artifacts/edbe5de71e26/gemma3-12b.html b/data/artifacts/edbe5de71e26/gemma3-12b.html
new file mode 100644
index 0000000..6a5acd1
--- /dev/null
+++ b/data/artifacts/edbe5de71e26/gemma3-12b.html
@@ -0,0 +1,209 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fluid Simulation</title>
+<style>
+body { margin: 0; overflow: hidden; background-color: black; }
+canvas { display: block; }
+#controls { position: absolute; top: 10px; left: 10px; color: white; font-family: sans-serif; }
+</style>
+</head>
+<body>
+
+<div id="controls">
+ <button id="clearBtn">Clear</button>
+ <input type="color" id="colorPicker" value="#ff0000">
+</div>
+
+<canvas id="fluidCanvas"></canvas>
+
+
+<script>
+const canvas = document.getElementById('fluidCanvas');
+const ctx = canvas.getContext('2d');
+const clearBtn = document.getElementById('clearBtn');
+const colorPicker = document.getElementById('colorPicker');
+
+let width = window.innerWidth;
+let height = window.innerHeight;
+
+canvas.width = width;
+canvas.height = height;
+
+
+// Fluid simulation parameters
+const densityResolution = 64; // Higher value -> finer grid, slower performance
+const velocityDamping = 0.995;
+const diffusionRate = 0.02;
+const viscosity = 0.01;
+
+let density = new Array(densityResolution * densityResolution).fill(0);
+let velocityX = new Array(densityResolution * densityResolution).fill(0);
+let velocityY = new Array(densityResolution * densityResolution).fill(0);
+
+
+// Helper functions for grid indexing
+function gridIndex(x, y) {
+ return Math.floor(x / (width / densityResolution)) + Math.floor(y / (height / densityResolution)) * densityResolution;
+}
+
+// Initialize the canvas with black background
+ctx.fillStyle = 'black';
+ctx.fillRect(0, 0, width, height);
+
+
+function update() {
+ // Advection step
+ let dx = width / densityResolution;
+ let dy = height / densityResolution;
+
+ for (let i = 0; i < densityResolution * densityResolution; ++i) {
+ let x = (i % densityResolution) * dx;
+ let y = Math.floor(i / densityResolution) * dy;
+
+ // Advect density and velocity
+ let ix = Math.max(0, Math.min(densityResolution - 1, Math.floor((x + velocityX[i]) / dx)));
+ let iy = Math.max(0, Math.min(densityResolution - 1, Math.floor((y + velocityY[i]) / dy)));
+ let index = ix + iy * densityResolution;
+
+ velocityX[i] *= velocityDamping;
+ velocityY[i] *= velocityDamping;
+
+ density[index] = (density[index] + density[i]) * 0.5; // Simple averaging for advection
+ }
+
+
+ // Diffusion step
+ for (let i = 0; i < densityResolution * densityResolution; ++i) {
+ let x = (i % densityResolution) * dx;
+ let y = Math.floor(i / densityResolution) * dy;
+
+ let neighbors = [
+ gridIndex(x - dx, y),
+ gridIndex(x + dx, y),
+ gridIndex(x, y - dy),
+ gridIndex(x, y + dy)
+ ];
+
+ density[i] = (density[i] + diffusionRate * (
+ density[neighbors[0]] + density[neighbors[1]] + density[neighbors[2]] + density[neighbors[3]]
+ )) / 5; // Simple averaging for diffusion.
+ }
+
+
+ // Viscosity step - smoothing velocity
+ for (let i = 0; i < densityResolution * densityResolution; ++i) {
+ let x = (i % densityResolution) * dx;
+ let y = Math.floor(i / densityResolution) * dy;
+
+ let neighbors = [
+ gridIndex(x - dx, y),
+ gridIndex(x + dx, y),
+ gridIndex(x, y - dy),
+ gridIndex(x, y + dy)
+ ];
+
+ velocityX[i] = (velocityX[i] + viscosity * (velocityX[neighbors[0]] + velocityX[neighbors[1]] + velocityX[neighbors[2]] + velocityX[neighbors[3]])) / 5;
+ velocityY[i] = (velocityY[i] + viscosity * (velocityY[neighbors[0]] + velocityY[neighbors[1]] + velocityY[neighbors[2]] + velocityY[neighbors[3]])) / 5;
+
+ }
+
+
+
+ // Rendering
+ ctx.fillStyle = 'rgba(0,0,0,0.9)'; // Dark background for trails
+ ctx.fillRect(0, 0, width, height);
+
+ ctx.globalCompositeOperation = 'screen';
+
+ for (let i = 0; i < densityResolution * densityResolution; ++i) {
+ let x = (i % densityResolution) * dx;
+ let y = Math.floor(i / densityResolution) * dy;
+
+ let colorValue = Math.max(0, Math.min(255, density[i] * 100)); // Scale density to color range
+ ctx.fillStyle = `rgba(${colorValue}, ${colorValue}, ${colorValue}, 1)`; // Adjust color as needed
+
+ ctx.fillRect(x, y, dx, dy);
+ }
+
+ ctx.globalCompositeOperation = 'destination-over'; // Back to normal blending for future additions.
+}
+
+
+function injectFluid(x, y, color) {
+ let dx = width / densityResolution;
+ let dy = height / densityResolution;
+
+ let ix = Math.floor(x / dx);
+ let iy = Math.floor(y / dy);
+ let index = ix + iy * densityResolution;
+
+ density[index] += 50; // Adjust injection strength as needed
+
+ velocityX[index] += 10 * (Math.random() - 0.5);
+ velocityY[index] += 10 * (Math.random() - 0.5);
+}
+
+
+
+let mouseX = 0;
+let mouseY = 0;
+
+canvas.addEventListener('mousemove', function(e) {
+ mouseX = e.clientX;
+ mouseY = e.clientY;
+});
+
+
+let animationFrameId;
+function animate() {
+ update();
+ animationFrameId = requestAnimationFrame(animate);
+}
+
+
+
+clearBtn.addEventListener('click', () => {
+ density.fill(0);
+ velocityX.fill(0);
+ velocityY.fill(0);
+
+ ctx.fillStyle = 'black';
+ ctx.fillRect(0, 0, width, height);
+});
+
+
+colorPicker.addEventListener('change', (e) => {
+ // Not used in this basic example but can be extended to change color based on picker value
+});
+
+
+
+window.addEventListener("resize", () => {
+ width = window.innerWidth;
+ height = window.innerHeight;
+ canvas.width = width;
+ canvas.height = height;
+});
+
+animate();
+
+
+// Periodically inject fluid from mouse position (optional)
+let lastInjectTime = 0;
+function periodicInject() {
+ const now = performance.now();
+ if (now - lastInjectTime > 200) { // Inject every 200ms, adjust as needed
+ injectFluid(mouseX, mouseY, colorPicker.value);
+ lastInjectTime = now;
+ }
+ requestAnimationFrame(periodicInject);
+}
+
+periodicInject();
+
+
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/edbe5de71e26/gemma3-12b.png b/data/artifacts/edbe5de71e26/gemma3-12b.png
new file mode 100644
index 0000000..373b222
Binary files /dev/null and b/data/artifacts/edbe5de71e26/gemma3-12b.png differ
diff --git a/data/artifacts/edbe5de71e26/gpt.html b/data/artifacts/edbe5de71e26/gpt.html
new file mode 100644
index 0000000..ddbcaed
--- /dev/null
+++ b/data/artifacts/edbe5de71e26/gpt.html
@@ -0,0 +1,378 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>Ink Fluid Simulation</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ background: #05060b;
+ color: #eee;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ height: 100%;
+ overflow: hidden;
+ }
+ #ui {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ z-index: 10;
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ background: rgba(10, 10, 15, 0.7);
+ padding: 6px 10px;
+ border-radius: 8px;
+ backdrop-filter: blur(4px);
+ -webkit-backdrop-filter: blur(4px);
+ box-shadow: 0 0 12px rgba(0,0,0,0.5);
+ }
+ #clearBtn {
+ border: none;
+ padding: 6px 12px;
+ border-radius: 6px;
+ background: #2b2f3c;
+ color: #eee;
+ cursor: pointer;
+ font-size: 13px;
+ }
+ #clearBtn:hover {
+ background: #404559;
+ }
+ #colorPicker {
+ width: 32px;
+ height: 24px;
+ padding: 0;
+ border-radius: 4px;
+ border: none;
+ background: transparent;
+ cursor: pointer;
+ }
+ #label {
+ font-size: 13px;
+ opacity: 0.85;
+ }
+ canvas {
+ display: block;
+ position: absolute;
+ inset: 0;
+ margin: auto;
+ background: radial-gradient(circle at 20% 20%, #141824 0, #05060b 55%, #000 100%);
+ }
+</style>
+</head>
+<body>
+<div id="ui">
+ <span id="label">Ink color:</span>
+ <input type="color" id="colorPicker" value="#33aaff">
+ <button id="clearBtn">Clear</button>
+</div>
+<canvas id="c"></canvas>
+
+<script>
+(function() {
+ const canvas = document.getElementById('c');
+ const ctx = canvas.getContext('2d');
+ const colorPicker = document.getElementById('colorPicker');
+ const clearBtn = document.getElementById('clearBtn');
+
+ let width, height;
+ function resize() {
+ width = window.innerWidth;
+ height = window.innerHeight;
+ canvas.width = width;
+ canvas.height = height;
+ }
+ window.addEventListener('resize', resize);
+ resize();
+
+ // Fluid parameters
+ const N = 128; // grid resolution
+ const iter = 12;
+ const diff = 0.00001;
+ const visc = 0.00001;
+ const dt = 0.016;
+
+ const size = (N + 2) * (N + 2);
+
+ // Arrays for velocity and density (RGB)
+ let u = new Float32Array(size), v = new Float32Array(size);
+ let uPrev = new Float32Array(size), vPrev = new Float32Array(size);
+
+ let r = new Float32Array(size), g = new Float32Array(size), b = new Float32Array(size);
+ let rPrev = new Float32Array(size), gPrev = new Float32Array(size), bPrev = new Float32Array(size);
+
+ function IX(x, y) {
+ return x + (N + 2) * y;
+ }
+
+ function addSource(x, s) {
+ for (let i = 0; i < size; i++) x[i] += dt * s[i];
+ }
+
+ function setBnd(b, x) {
+ const Np2 = N + 2;
+ for (let i = 1; i <= N; i++) {
+ x[IX(0, i)] = b === 1 ? -x[IX(1, i)] : x[IX(1, i)];
+ x[IX(N + 1, i)] = b === 1 ? -x[IX(N, i)] : x[IX(N, i)];
+ x[IX(i, 0)] = b === 2 ? -x[IX(i, 1)] : x[IX(i, 1)];
+ x[IX(i, N + 1)] = b === 2 ? -x[IX(i, N)] : x[IX(i, N)];
+ }
+ x[IX(0, 0)] = 0.5 * (x[IX(1, 0)] + x[IX(0, 1)]);
+ x[IX(0, N + 1)] = 0.5 * (x[IX(1, N + 1)] + x[IX(0, N)]);
+ x[IX(N + 1, 0)] = 0.5 * (x[IX(N, 0)] + x[IX(N + 1, 1)]);
+ x[IX(N + 1, N + 1)] = 0.5 * (x[IX(N, N + 1)] + x[IX(N + 1, N)]);
+ }
+
+ function linSolve(b, x, x0, a, c) {
+ for (let k = 0; k < iter; k++) {
+ for (let j = 1; j <= N; j++) {
+ let idx = IX(1, j);
+ for (let i = 1; i <= N; i++, idx++) {
+ x[idx] = (x0[idx] + a * (
+ x[idx - 1] +
+ x[idx + 1] +
+ x[idx - (N + 2)] +
+ x[idx + (N + 2)]
+ )) / c;
+ }
+ }
+ setBnd(b, x);
+ }
+ }
+
+ function diffuse(b, x, x0, diff) {
+ const a = dt * diff * N * N;
+ linSolve(b, x, x0, a, 1 + 4 * a);
+ }
+
+ function advect(b, d, d0, u, v) {
+ const dt0 = dt * N;
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ let x = i - dt0 * u[IX(i, j)];
+ let y = j - dt0 * v[IX(i, j)];
+ if (x < 0.5) x = 0.5;
+ if (x > N + 0.5) x = N + 0.5;
+ const i0 = x | 0;
+ const i1 = i0 + 1;
+ if (y < 0.5) y = 0.5;
+ if (y > N + 0.5) y = N + 0.5;
+ const j0 = y | 0;
+ const j1 = j0 + 1;
+ const s1 = x - i0;
+ const s0 = 1 - s1;
+ const t1 = y - j0;
+ const t0 = 1 - t1;
+ const idx = IX(i, j);
+ d[idx] = s0 * (t0 * d0[IX(i0, j0)] + t1 * d0[IX(i0, j1)]) +
+ s1 * (t0 * d0[IX(i1, j0)] + t1 * d0[IX(i1, j1)]);
+ }
+ }
+ setBnd(b, d);
+ }
+
+ function project(u, v, p, div) {
+ const Np2 = N + 2;
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ const idx = IX(i, j);
+ div[idx] = -0.5 * (
+ u[idx + 1] - u[idx - 1] +
+ v[idx + Np2] - v[idx - Np2]
+ ) / N;
+ p[idx] = 0;
+ }
+ }
+ setBnd(0, div);
+ setBnd(0, p);
+ linSolve(0, p, div, 1, 4);
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ const idx = IX(i, j);
+ u[idx] -= 0.5 * N * (p[idx + 1] - p[idx - 1]);
+ v[idx] -= 0.5 * N * (p[idx + Np2] - p[idx - Np2]);
+ }
+ }
+ setBnd(1, u);
+ setBnd(2, v);
+ }
+
+ function velStep(u, v, u0, v0) {
+ addSource(u, u0);
+ addSource(v, v0);
+ // swap
+ [u0, u] = [u, u0];
+ diffuse(1, u, u0, visc);
+ [v0, v] = [v, v0];
+ diffuse(2, v, v0, visc);
+ project(u, v, u0, v0);
+ [u0, u] = [u, u0];
+ [v0, v] = [v, v0];
+ advect(1, u, u0, u0, v0);
+ advect(2, v, v0, u0, v0);
+ project(u, v, u0, v0);
+ // clear source
+ u0.fill(0);
+ v0.fill(0);
+ }
+
+ function densStep(d, d0, u, v) {
+ addSource(d, d0);
+ [d0, d] = [d, d0];
+ diffuse(0, d, d0, diff);
+ [d0, d] = [d, d0];
+ advect(0, d, d0, u, v);
+ d0.fill(0);
+ }
+
+ function step() {
+ velStep(u, v, uPrev, vPrev);
+ densStep(r, rPrev, u, v);
+ densStep(g, gPrev, u, v);
+ densStep(b, bPrev, u, v);
+ }
+
+ // Mouse interaction
+ let isDown = false;
+ let lastX = 0, lastY = 0;
+ let currentColor = { r: 0.2, g: 0.7, b: 1.0 };
+
+ function screenToGrid(x, y) {
+ const gx = (x / width) * N + 1;
+ const gy = (y / height) * N + 1;
+ return { gx, gy };
+ }
+
+ function inject(x, y, vx, vy) {
+ const { gx, gy } = screenToGrid(x, y);
+ const rad = 2;
+ const ig = Math.max(1, Math.min(N, Math.round(gx)));
+ const jg = Math.max(1, Math.min(N, Math.round(gy)));
+ for (let dj = -rad; dj <= rad; dj++) {
+ const j = jg + dj;
+ if (j < 1 || j > N) continue;
+ for (let di = -rad; di <= rad; di++) {
+ const i = ig + di;
+ if (i < 1 || i > N) continue;
+ const idx = IX(i, j);
+ const falloff = Math.max(0, 1 - (di*di + dj*dj) / (rad*rad + 0.01));
+ const amt = 80 * falloff;
+ rPrev[idx] += currentColor.r * amt;
+ gPrev[idx] += currentColor.g * amt;
+ bPrev[idx] += currentColor.b * amt;
+ uPrev[idx] += vx * 200 * falloff;
+ vPrev[idx] += vy * 200 * falloff;
+ }
+ }
+ }
+
+ canvas.addEventListener('mousedown', e => {
+ isDown = true;
+ lastX = e.clientX;
+ lastY = e.clientY;
+ });
+ window.addEventListener('mouseup', () => { isDown = false; });
+ window.addEventListener('mouseleave', () => { isDown = false; });
+ canvas.addEventListener('mousemove', e => {
+ if (!isDown) {
+ lastX = e.clientX;
+ lastY = e.clientY;
+ return;
+ }
+ const x = e.clientX;
+ const y = e.clientY;
+ const vx = (x - lastX) / width;
+ const vy = (y - lastY) / height;
+ inject(x, y, vx, vy);
+ lastX = x;
+ lastY = y;
+ });
+
+ colorPicker.addEventListener('input', () => {
+ const hex = colorPicker.value;
+ const r8 = parseInt(hex.slice(1,3),16) / 255;
+ const g8 = parseInt(hex.slice(3,5),16) / 255;
+ const b8 = parseInt(hex.slice(5,7),16) / 255;
+ currentColor = { r: r8, g: g8, b: b8 };
+ });
+
+ clearBtn.addEventListener('click', () => {
+ r.fill(0); g.fill(0); b.fill(0);
+ rPrev.fill(0); gPrev.fill(0); bPrev.fill(0);
+ u.fill(0); v.fill(0);
+ uPrev.fill(0); vPrev.fill(0);
+ });
+
+ function render() {
+ const img = ctx.createImageData(width, height);
+ const data = img.data;
+
+ for (let y = 0; y < height; y++) {
+ const gy = 1 + (y + 0.5) / height * N;
+ const j0 = Math.max(1, Math.min(N, Math.floor(gy)));
+ const j1 = Math.max(1, Math.min(N, j0 + 1));
+ const tj = gy - j0;
+
+ for (let x = 0; x < width; x++) {
+ const gx = 1 + (x + 0.5) / width * N;
+ const i0 = Math.max(1, Math.min(N, Math.floor(gx)));
+ const i1 = Math.max(1, Math.min(N, i0 + 1));
+ const ti = gx - i0;
+
+ const idx00 = IX(i0, j0);
+ const idx10 = IX(i1, j0);
+ const idx01 = IX(i0, j1);
+ const idx11 = IX(i1, j1);
+
+ const w00 = (1 - ti) * (1 - tj);
+ const w10 = ti * (1 - tj);
+ const w01 = (1 - ti) * tj;
+ const w11 = ti * tj;
+
+ let R = r[idx00] * w00 + r[idx10] * w10 + r[idx01] * w01 + r[idx11] * w11;
+ let G = g[idx00] * w00 + g[idx10] * w10 + g[idx01] * w01 + g[idx11] * w11;
+ let B = b[idx00] * w00 + b[idx10] * w10 + b[idx01] * w01 + b[idx11] * w11;
+
+ // Soft fade / tone mapping
+ const s = 0.015;
+ R = 1 - Math.exp(-R * s);
+ G = 1 - Math.exp(-G * s);
+ B = 1 - Math.exp(-B * s);
+
+ const base = (y * width + x) * 4;
+ data[base] = Math.max(0, Math.min(255, R * 255));
+ data[base + 1] = Math.max(0, Math.min(255, G * 255));
+ data[base + 2] = Math.max(0, Math.min(255, B * 255));
+ data[base + 3] = 255;
+ }
+ }
+
+ ctx.putImageData(img, 0, 0);
+ // Vignette overlay
+ const grd = ctx.createRadialGradient(
+ width * 0.5, height * 0.4, 0,
+ width * 0.5, height * 0.5, Math.max(width, height) * 0.7
+ );
+ grd.addColorStop(0, 'rgba(0,0,0,0)');
+ grd.addColorStop(1, 'rgba(0,0,0,0.65)');
+ ctx.fillStyle = grd;
+ ctx.fillRect(0, 0, width, height);
+ }
+
+ let lastTime = performance.now();
+ function loop(now) {
+ const elapsed = now - lastTime;
+ if (elapsed > 1000 / 60 - 1) {
+ step();
+ render();
+ lastTime = now;
+ }
+ requestAnimationFrame(loop);
+ }
+ requestAnimationFrame(loop);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/edbe5de71e26/gpt.png b/data/artifacts/edbe5de71e26/gpt.png
new file mode 100644
index 0000000..1c9cdd9
Binary files /dev/null and b/data/artifacts/edbe5de71e26/gpt.png differ
diff --git a/data/artifacts/edbe5de71e26/grok.html b/data/artifacts/edbe5de71e26/grok.html
new file mode 100644
index 0000000..0a272f5
--- /dev/null
+++ b/data/artifacts/edbe5de71e26/grok.html
@@ -0,0 +1,538 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Fluid Ink Simulation</title>
+<style>
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+ html, body {
+ width: 100%; height: 100%; overflow: hidden;
+ background: #0a0a0f;
+ font-family: system-ui, -apple-system, sans-serif;
+ }
+ canvas {
+ display: block;
+ width: 100%; height: 100%;
+ cursor: crosshair;
+ }
+ #ui {
+ position: fixed;
+ top: 16px;
+ left: 16px;
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ z-index: 10;
+ background: rgba(20, 20, 30, 0.85);
+ padding: 10px 14px;
+ border-radius: 10px;
+ border: 1px solid rgba(255,255,255,0.08);
+ backdrop-filter: blur(8px);
+ }
+ #ui label {
+ color: #aaa;
+ font-size: 12px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+ #colorPicker {
+ width: 36px; height: 28px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ background: transparent;
+ padding: 0;
+ }
+ #colorPicker::-webkit-color-swatch-wrapper { padding: 0; }
+ #colorPicker::-webkit-color-swatch {
+ border: 2px solid rgba(255,255,255,0.2);
+ border-radius: 6px;
+ }
+ button {
+ background: #2a2a3a;
+ color: #e0e0e8;
+ border: 1px solid rgba(255,255,255,0.12);
+ border-radius: 6px;
+ padding: 6px 14px;
+ font-size: 13px;
+ cursor: pointer;
+ transition: background 0.15s;
+ }
+ button:hover { background: #3a3a4a; }
+ button:active { background: #1a1a2a; }
+ #hint {
+ position: fixed;
+ bottom: 16px;
+ left: 50%;
+ transform: translateX(-50%);
+ color: rgba(255,255,255,0.25);
+ font-size: 12px;
+ pointer-events: none;
+ z-index: 10;
+ }
+</style>
+</head>
+<body>
+<div id="ui">
+ <label>Ink <input type="color" id="colorPicker" value="#00e5ff"></label>
+ <button id="clearBtn">Clear</button>
+</div>
+<div id="hint">Drag to paint swirling ink</div>
+<canvas id="c"></canvas>
+<script>
+(function () {
+ const canvas = document.getElementById('c');
+ const ctx = canvas.getContext('2d');
+ const colorPicker = document.getElementById('colorPicker');
+ const clearBtn = document.getElementById('clearBtn');
+
+ // Simulation resolution (cells). Higher = more detail, slower.
+ const N = 128;
+ const N2 = (N + 2) * (N + 2);
+ const iter = 4; // Gauss-Seidel iterations
+
+ let width, height, cellW, cellH;
+ let imageData;
+
+ // Fields: velocity + RGB density (with 1-cell border)
+ let u = new Float32Array(N2);
+ let v = new Float32Array(N2);
+ let u0 = new Float32Array(N2);
+ let v0 = new Float32Array(N2);
+ let r = new Float32Array(N2);
+ let g = new Float32Array(N2);
+ let b = new Float32Array(N2);
+ let r0 = new Float32Array(N2);
+ let g0 = new Float32Array(N2);
+ let b0 = new Float32Array(N2);
+
+ function IX(i, j) { return i + (N + 2) * j; }
+
+ function resize() {
+ width = window.innerWidth;
+ height = window.innerHeight;
+ canvas.width = width;
+ canvas.height = height;
+ cellW = width / N;
+ cellH = height / N;
+ imageData = ctx.createImageData(width, height);
+ }
+ window.addEventListener('resize', resize);
+ resize();
+
+ function setBnd(b, x) {
+ for (let i = 1; i <= N; i++) {
+ x[IX(0, i)] = b === 1 ? -x[IX(1, i)] : x[IX(1, i)];
+ x[IX(N + 1, i)] = b === 1 ? -x[IX(N, i)] : x[IX(N, i)];
+ x[IX(i, 0)] = b === 2 ? -x[IX(i, 1)] : x[IX(i, 1)];
+ x[IX(i, N + 1)] = b === 2 ? -x[IX(i, N)] : x[IX(i, N)];
+ }
+ x[IX(0, 0)] = 0.5 * (x[IX(1, 0)] + x[IX(0, 1)]);
+ x[IX(0, N + 1)] = 0.5 * (x[IX(1, N + 1)] + x[IX(0, N)]);
+ x[IX(N + 1, 0)] = 0.5 * (x[IX(N, 0)] + x[IX(N + 1, 1)]);
+ x[IX(N + 1, N + 1)] = 0.5 * (x[IX(N, N + 1)] + x[IX(N + 1, N)]);
+ }
+
+ function linSolve(b, x, x0, a, c) {
+ const cRecip = 1 / c;
+ for (let k = 0; k < iter; k++) {
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ x[IX(i, j)] =
+ (x0[IX(i, j)] +
+ a * (x[IX(i - 1, j)] + x[IX(i + 1, j)] +
+ x[IX(i, j - 1)] + x[IX(i, j + 1)])) * cRecip;
+ }
+ }
+ setBnd(b, x);
+ }
+ }
+
+ function diffuse(b, x, x0, diff, dt) {
+ const a = dt * diff * N * N;
+ linSolve(b, x, x0, a, 1 + 4 * a);
+ }
+
+ function project(u, v, p, div) {
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ div[IX(i, j)] =
+ (-0.5 *
+ (u[IX(i + 1, j)] - u[IX(i - 1, j)] +
+ v[IX(i, j + 1)] - v[IX(i, j - 1)])) / N;
+ p[IX(i, j)] = 0;
+ }
+ }
+ setBnd(0, div);
+ setBnd(0, p);
+ linSolve(0, p, div, 1, 4);
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ u[IX(i, j)] -= 0.5 * N * (p[IX(i + 1, j)] - p[IX(i - 1, j)]);
+ v[IX(i, j)] -= 0.5 * N * (p[IX(i, j + 1)] - p[IX(i, j - 1)]);
+ }
+ }
+ setBnd(1, u);
+ setBnd(2, v);
+ }
+
+ function advect(b, d, d0, u, v, dt) {
+ const dt0 = dt * N;
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ let x = i - dt0 * u[IX(i, j)];
+ let y = j - dt0 * v[IX(i, j)];
+ if (x < 0.5) x = 0.5;
+ if (x > N + 0.5) x = N + 0.5;
+ const i0 = x | 0;
+ const i1 = i0 + 1;
+ if (y < 0.5) y = 0.5;
+ if (y > N + 0.5) y = N + 0.5;
+ const j0 = y | 0;
+ const j1 = j0 + 1;
+ const s1 = x - i0;
+ const s0 = 1 - s1;
+ const t1 = y - j0;
+ const t0 = 1 - t1;
+ d[IX(i, j)] =
+ s0 * (t0 * d0[IX(i0, j0)] + t1 * d0[IX(i0, j1)]) +
+ s1 * (t0 * d0[IX(i1, j0)] + t1 * d0[IX(i1, j1)]);
+ }
+ }
+ setBnd(b, d);
+ }
+
+ function densStep(dens, dens0, u, v, diff, dt) {
+ // dens0 already has sources added
+ addSource(dens, dens0, dt);
+ // swap dens <-> dens0 via pointer dance with temps
+ let tmp;
+ tmp = dens0; dens0 = dens; dens = tmp;
+ diffuse(0, dens, dens0, diff, dt);
+ tmp = dens0; dens0 = dens; dens = tmp;
+ advect(0, dens, dens0, u, v, dt);
+ return dens; // may have been swapped — we handle carefully below
+ }
+
+ function addSource(x, s, dt) {
+ for (let i = 0; i < N2; i++) x[i] += dt * s[i];
+ }
+
+ function velStep(u, v, u0, v0, visc, dt) {
+ addSource(u, u0, dt);
+ addSource(v, v0, dt);
+ let tmp;
+ tmp = u0; u0 = u; u = tmp;
+ diffuse(1, u, u0, visc, dt);
+ tmp = v0; v0 = v; v = tmp;
+ diffuse(2, v, v0, visc, dt);
+ project(u, v, u0, v0);
+ tmp = u0; u0 = u; u = tmp;
+ tmp = v0; v0 = v; v = tmp;
+ advect(1, u, u0, u0, v0, dt);
+ advect(2, v, v0, u0, v0, dt);
+ project(u, v, u0, v0);
+ // copy results back if swapped — use explicit approach instead
+ }
+
+ // Cleaner step functions that mutate in place with fixed buffers
+ function stepVelocity(dt) {
+ // u0, v0 hold forces; clear them after adding
+ addSource(u, u0, dt);
+ addSource(v, v0, dt);
+ u0.fill(0);
+ v0.fill(0);
+
+ // diffuse
+ let tmpU = new Float32Array(u);
+ let tmpV = new Float32Array(v);
+ diffuse(1, u, tmpU, VISC, dt);
+ diffuse(2, v, tmpV, VISC, dt);
+ project(u, v, u0, v0);
+
+ // advect
+ tmpU = new Float32Array(u);
+ tmpV = new Float32Array(v);
+ // u0,v0 reused as temp pressure/div — already zeroed by project partially
+ // Store current vel for advection lookup
+ const uAdv = tmpU;
+ const vAdv = tmpV;
+ advect(1, u, uAdv, uAdv, vAdv, dt);
+ advect(2, v, vAdv, uAdv, vAdv, dt);
+ project(u, v, u0, v0);
+ u0.fill(0);
+ v0.fill(0);
+ }
+
+ function stepDensity(dens, densSrc, dt) {
+ addSource(dens, densSrc, dt);
+ densSrc.fill(0);
+ const tmp = new Float32Array(dens);
+ diffuse(0, dens, tmp, DIFF, dt);
+ const tmp2 = new Float32Array(dens);
+ advect(0, dens, tmp2, u, v, dt);
+ }
+
+ // Avoid per-frame allocations — use preallocated temps
+ const tmp1 = new Float32Array(N2);
+ const tmp2 = new Float32Array(N2);
+ const tmp3 = new Float32Array(N2);
+ const tmp4 = new Float32Array(N2);
+
+ function stepVelocityFast(dt) {
+ addSource(u, u0, dt);
+ addSource(v, v0, dt);
+ u0.fill(0);
+ v0.fill(0);
+
+ // diffuse u
+ tmp1.set(u);
+ diffuse(1, u, tmp1, VISC, dt);
+ tmp2.set(v);
+ diffuse(2, v, tmp2, VISC, dt);
+ project(u, v, tmp3, tmp4);
+
+ // advect
+ tmp1.set(u);
+ tmp2.set(v);
+ advect(1, u, tmp1, tmp1, tmp2, dt);
+ advect(2, v, tmp2, tmp1, tmp2, dt);
+ project(u, v, tmp3, tmp4);
+ }
+
+ function stepDensityFast(dens, densSrc, dt) {
+ addSource(dens, densSrc, dt);
+ densSrc.fill(0);
+ tmp1.set(dens);
+ diffuse(0, dens, tmp1, DIFF, dt);
+ tmp1.set(dens);
+ advect(0, dens, tmp1, u, v, dt);
+ }
+
+ const VISC = 0.00001;
+ const DIFF = 0.00002;
+ const DT = 0.1;
+ const FORCE = 80;
+ const SOURCE = 120;
+
+ // Mouse state
+ let mouseDown = false;
+ let prevMX = 0, prevMY = 0;
+ let curMX = 0, curMY = 0;
+
+ function canvasPos(e) {
+ const rect = canvas.getBoundingClientRect();
+ let clientX, clientY;
+ if (e.touches && e.touches.length) {
+ clientX = e.touches[0].clientX;
+ clientY = e.touches[0].clientY;
+ } else {
+ clientX = e.clientX;
+ clientY = e.clientY;
+ }
+ return {
+ x: (clientX - rect.left) * (canvas.width / rect.width),
+ y: (clientY - rect.top) * (canvas.height / rect.height)
+ };
+ }
+
+ function inject(mx, my, pmx, pmy) {
+ const i = Math.max(1, Math.min(N, (mx / width) * N | 0));
+ const j = Math.max(1, Math.min(N, (my / height) * N | 0));
+ const dx = mx - pmx;
+ const dy = my - pmy;
+
+ // Velocity from mouse movement
+ for (let oj = -2; oj <= 2; oj++) {
+ for (let oi = -2; oi <= 2; oi++) {
+ const ci = i + oi;
+ const cj = j + oj;
+ if (ci < 1 || ci > N || cj < 1 || cj > N) continue;
+ const falloff = Math.exp(-(oi * oi + oj * oj) * 0.4);
+ u0[IX(ci, cj)] += dx * FORCE * falloff;
+ v0[IX(ci, cj)] += dy * FORCE * falloff;
+ }
+ }
+
+ // Parse color
+ const hex = colorPicker.value;
+ const cr = parseInt(hex.slice(1, 3), 16) / 255;
+ const cg = parseInt(hex.slice(3, 5), 16) / 255;
+ const cb = parseInt(hex.slice(5, 7), 16) / 255;
+
+ for (let oj = -2; oj <= 2; oj++) {
+ for (let oi = -2; oi <= 2; oi++) {
+ const ci = i + oi;
+ const cj = j + oj;
+ if (ci < 1 || ci > N || cj < 1 || cj > N) continue;
+ const falloff = Math.exp(-(oi * oi + oj * oj) * 0.35);
+ r0[IX(ci, cj)] += SOURCE * cr * falloff;
+ g0[IX(ci, cj)] += SOURCE * cg * falloff;
+ b0[IX(ci, cj)] += SOURCE * cb * falloff;
+ }
+ }
+ }
+
+ canvas.addEventListener('mousedown', (e) => {
+ mouseDown = true;
+ const p = canvasPos(e);
+ prevMX = curMX = p.x;
+ prevMY = curMY = p.y;
+ });
+ canvas.addEventListener('mousemove', (e) => {
+ const p = canvasPos(e);
+ curMX = p.x;
+ curMY = p.y;
+ if (mouseDown) {
+ inject(curMX, curMY, prevMX, prevMY);
+ prevMX = curMX;
+ prevMY = curMY;
+ }
+ });
+ window.addEventListener('mouseup', () => { mouseDown = false; });
+
+ // Touch
+ canvas.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ mouseDown = true;
+ const p = canvasPos(e);
+ prevMX = curMX = p.x;
+ prevMY = curMY = p.y;
+ }, { passive: false });
+ canvas.addEventListener('touchmove', (e) => {
+ e.preventDefault();
+ const p = canvasPos(e);
+ curMX = p.x;
+ curMY = p.y;
+ if (mouseDown) {
+ inject(curMX, curMY, prevMX, prevMY);
+ prevMX = curMX;
+ prevMY = curMY;
+ }
+ }, { passive: false });
+ canvas.addEventListener('touchend', () => { mouseDown = false; });
+
+ clearBtn.addEventListener('click', () => {
+ u.fill(0); v.fill(0);
+ u0.fill(0); v0.fill(0);
+ r.fill(0); g.fill(0); b.fill(0);
+ r0.fill(0); g0.fill(0); b0.fill(0);
+ });
+
+ function render() {
+ // Render density field stretched to canvas
+ // Sample grid and bilinear upscale into imageData
+ const data = imageData.data;
+ const scaleX = N / width;
+ const scaleY = N / height;
+
+ for (let py = 0; py < height; py++) {
+ const gy = py * scaleY + 1;
+ const j0 = Math.min(N, Math.max(1, gy | 0));
+ const j1 = Math.min(N, j0 + 1);
+ const ty = gy - j0;
+ const ty0 = 1 - ty;
+ for (let px = 0; px < width; px++) {
+ const gx = px * scaleX + 1;
+ const i0 = Math.min(N, Math.max(1, gx | 0));
+ const i1 = Math.min(N, i0 + 1);
+ const tx = gx - i0;
+ const tx0 = 1 - tx;
+
+ const idx00 = IX(i0, j0);
+ const idx10 = IX(i1, j0);
+ const idx01 = IX(i0, j1);
+ const idx11 = IX(i1, j1);
+
+ let rv = tx0 * (ty0 * r[idx00] + ty * r[idx01]) + tx * (ty0 * r[idx10] + ty * r[idx11]);
+ let gv = tx0 * (ty0 * g[idx00] + ty * g[idx01]) + tx * (ty0 * g[idx10] + ty * g[idx11]);
+ let bv = tx0 * (ty0 * b[idx00] + ty * b[idx01]) + tx * (ty0 * b[idx10] + ty * b[idx11]);
+
+ // Soft glow / tone map
+ rv = 1 - Math.exp(-rv * 0.9);
+ gv = 1 - Math.exp(-gv * 0.9);
+ bv = 1 - Math.exp(-bv * 0.9);
+
+ const off = (py * width + px) * 4;
+ data[off] = (rv * 255) | 0;
+ data[off + 1] = (gv * 255) | 0;
+ data[off + 2] = (bv * 255) | 0;
+ data[off + 3] = 255;
+ }
+ }
+ ctx.putImageData(imageData, 0, 0);
+ }
+
+ // Faster render: draw at grid res then scale up with canvas smoothing
+ const lowCanvas = document.createElement('canvas');
+ lowCanvas.width = N;
+ lowCanvas.height = N;
+ const lowCtx = lowCanvas.getContext('2d');
+ const lowImg = lowCtx.createImageData(N, N);
+
+ function renderFast() {
+ const data = lowImg.data;
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ const idx = IX(i, j);
+ let rv = r[idx], gv = g[idx], bv = b[idx];
+ rv = 1 - Math.exp(-rv * 0.85);
+ gv = 1 - Math.exp(-gv * 0.85);
+ bv = 1 - Math.exp(-bv * 0.85);
+ const off = ((j - 1) * N + (i - 1)) * 4;
+ data[off] = (rv * 255) | 0;
+ data[off + 1] = (gv * 255) | 0;
+ data[off + 2] = (bv * 255) | 0;
+ data[off + 3] = 255;
+ }
+ }
+ lowCtx.putImageData(lowImg, 0, 0);
+ ctx.imageSmoothingEnabled = true;
+ ctx.imageSmoothingQuality = 'high';
+ ctx.fillStyle = '#0a0a0f';
+ ctx.fillRect(0, 0, width, height);
+ ctx.drawImage(lowCanvas, 0, 0, width, height);
+ }
+
+ function fadeDensities() {
+ // Slow fade so ink doesn't permanently fill the screen
+ const fade = 0.994;
+ for (let i = 0; i < N2; i++) {
+ r[i] *= fade;
+ g[i] *= fade;
+ b[i] *= fade;
+ }
+ }
+
+ function loop() {
+ stepVelocityFast(DT);
+ stepDensityFast(r, r0, DT);
+ stepDensityFast(g, g0, DT);
+ stepDensityFast(b, b0, DT);
+ fadeDensities();
+ renderFast();
+ requestAnimationFrame(loop);
+ }
+
+ // Seed a gentle initial swirl so it's not empty
+ (function seed() {
+ const cx = N / 2, cy = N / 2;
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ const dx = i - cx, dy = j - cy;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < N * 0.15 && dist > 2) {
+ u[IX(i, j)] += -dy * 0.4;
+ v[IX(i, j)] += dx * 0.4;
+ }
+ }
+ }
+ })();
+
+ requestAnimationFrame(loop);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/edbe5de71e26/grok.png b/data/artifacts/edbe5de71e26/grok.png
new file mode 100644
index 0000000..c2a4b68
Binary files /dev/null and b/data/artifacts/edbe5de71e26/grok.png differ
diff --git a/data/artifacts/edbe5de71e26/hermes3-8b.html b/data/artifacts/edbe5de71e26/hermes3-8b.html
new file mode 100644
index 0000000..8743581
--- /dev/null
+++ b/data/artifacts/edbe5de71e26/hermes3-8b.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Fluid Ink Simulation</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ }
+
+ canvas {
+ display: block;
+ }
+
+ #info {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ background-color: rgba(255, 255, 255, 0.7);
+ padding: 5px;
+ border-radius: 5px;
+ }
+ </style>
+</head>
+<body>
+ <div id="info">
+ Click and drag to inject ink.<br/>
+ Click "Clear" to reset the canvas.
+ </div>
+
+ <canvas id="canvas"></canvas>
+ <input type="button" value="Clear" onclick="clearCanvas()">
+ <input type="color" id="colorPicker" onchange="updateColor()">
+
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+
+ const width = window.innerWidth;
+ const height = window.innerHeight;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ let particles = [];
+ let color = '#0000FF';
+
+ function random(min, max) {
+ return Math.random() * (max - min) + min;
+ }
+
+ class Particle {
+ constructor(x, y, radius, color) {
+ this.x = x;
+ this.y = y;
+ this.radius = radius;
+ this.color = color;
+ this.speedX = 0;
+ this.speedY = 0;
+ }
+
+ update() {
+ this.speedX *= 0.95;
+ this.speedY *= 0.95;
+
+ this.x += this.speedX;
+ this.y += this.speedY;
+
+ if (this.radius > 1) {
+ this.radius *= 0.99;
+ }
+ }
+
+ draw() {
+ ctx.fillStyle = this.color;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ function mouseDragged(x, y) {
+ let radius = random(5, 20);
+ particles.push(new Particle(x, y, radius, color));
+ }
+
+ canvas.addEventListener('mousemove', (e) => {
+ mouseDragged(e.offsetX, e.offsetYY);
+ });
+
+ function clearCanvas() {
+ ctx.clearRect(0, 0, width, height);
+ particles = [];
+ }
+
+ function updateColor() {
+ color = `#${document.getElementById('colorPicker').value}`;
+ }
+
+ function render() {
+ // Clear canvas
+ ctx.fillStyle = '#000';
+ ctx.fillRect(0, 0, width, height);
+
+ // Update and draw particles
+ for (let i = particles.length - 1; i >= 0; i--) {
+ particles[i].update();
+ particles[i].draw();
+
+ if (particles[i].radius <= 0.5) {
+ particles.splice(i, 1);
+ }
+ }
+
+ requestAnimationFrame(render);
+ }
+
+ render();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/edbe5de71e26/hermes3-8b.png b/data/artifacts/edbe5de71e26/hermes3-8b.png
new file mode 100644
index 0000000..13ad523
Binary files /dev/null and b/data/artifacts/edbe5de71e26/hermes3-8b.png differ
diff --git a/data/artifacts/edbe5de71e26/hf-qwen-coder-32b.html b/data/artifacts/edbe5de71e26/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..dcb9531
--- /dev/null
+++ b/data/artifacts/edbe5de71e26/hf-qwen-coder-32b.html
@@ -0,0 +1,224 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Fluid Ink Simulation</title>
+<style>
+ body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #000; }
+ canvas { border: 2px solid #fff; cursor: crosshair; }
+ .controls { position: absolute; top: 10px; left: 10px; color: white; font-family: Arial, sans-serif; }
+ input[type="color"], button { margin-right: 5px; }
+</style>
+</head>
+<body>
+<div class="controls">
+ <input type="color" id="colorPicker" value="#ff0000">
+ <button onclick="clearCanvas()">Clear</button>
+</div>
+<canvas id="inkCanvas"></canvas>
+<script>
+const canvas = document.getElementById('inkCanvas');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth - 40;
+canvas.height = window.innerHeight - 40;
+
+const density = 16;
+const nx = Math.floor(canvas.width / density) + 1;
+const ny = Math.floor(canvas.height / density) + 1;
+
+let u = Array(nx * ny).fill(0);
+let v = Array(nx * ny).fill(0);
+let du = Array(nx * ny).fill(0);
+let dv = Array(nx * ny).fill(0);
+let colorGrid = Array(nx * ny).fill([0, 0, 0]);
+
+const diff = 0.0001;
+const visc = 0.0001;
+
+function idx(x, y) { return x + y * nx; }
+
+function setBnd(b, u) {
+ for (let i = 1; i < ny - 1; i++) {
+ u[idx(0, i)] = b === 2 ? -u[idx(1, i)] : u[idx(1, i)];
+ u[idx(nx - 1, i)] = b === 2 ? -u[idx(nx - 2, i)] : u[idx(nx - 2, i)];
+ }
+ for (let i = 1; i < nx - 1; i++) {
+ u[idx(i, 0)] = b === 1 ? -u[idx(i, 1)] : u[idx(i, 1)];
+ u[idx(i, ny - 1)] = b === 1 ? -u[idx(i, ny - 2)] : u[idx(i, ny - 2)];
+ }
+ u[idx(0, 0)] = 0.5 * (u[idx(1, 0)] + u[idx(0, 1)]);
+ u[idx(nx - 1, 0)] = 0.5 * (u[idx(nx - 2, 0)] + u[idx(nx - 1, 1)]);
+ u[idx(0, ny - 1)] = 0.5 * (u[idx(1, ny - 1)] + u[idx(0, ny - 2)]);
+ u[idx(nx - 1, ny - 1)] = 0.5 * (u[idx(nx - 2, ny - 1)] + u[idx(nx - 1, ny - 2)]);
+}
+
+function linSolve(b, x, x0, a, c) {
+ for (let i = 1; i < ny - 1; i++) {
+ for (let j = 1; j < nx - 1; j++) {
+ x[idx(j, i)] = (x0[idx(j, i)] + a * (x[idx(j - 1, i)] + x[idx(j + 1, i)] + x[idx(j, i - 1)] + x[idx(j, i + 1)])) / c;
+ }
+ }
+ setBnd(b, x);
+}
+
+function diffuse(b, x, x0, diff, dt) {
+ let a = diff * dt * (nx - 2) * (ny - 2);
+ linSolve(b, x, x0, a, 1 + 4 * a);
+}
+
+function advect(b, d, d0, u, v, dt) {
+ for (let i = 1; i < ny - 1; i++) {
+ for (let j = 1; j < nx - 1; j++) {
+ let x = j - dt * nx * u[idx(j, i)];
+ let y = i - dt * ny * v[idx(j, i)];
+
+ if (x < 0.5) x = 0.5;
+ if (x > nx + 0.5) x = nx + 0.5;
+ intx = Math.floor(x);
+ fx = x - intx;
+
+ if (y < 0.5) y = 0.5;
+ if (y > ny + 0.5) y = ny + 0.5;
+ inty = Math.floor(y);
+ fy = y - inty;
+
+ let i0 = idx(intx, inty);
+ let i1 = idx(intx + 1, inty);
+ let i2 = idx(intx, inty + 1);
+ let i3 = idx(intx + 1, inty + 1);
+
+ d[idx(j, i)] =
+ (1 - fx) * ((1 - fy) * d0[i0] + fy * d0[i2]) +
+ fx * ((1 - fy) * d0[i1] + fy * d0[i3]);
+ }
+ }
+ setBnd(b, d);
+}
+
+function project(u, v, p, div) {
+ for (let i = 1; i < ny - 1; i++) {
+ for (let j = 1; j < nx - 1; j++) {
+ div[idx(j, i)] = -0.5 * (u[idx(j + 1, i)] - u[idx(j - 1, i)] + v[idx(j, i + 1)] - v[idx(j, i - 1)]) / density;
+ p[idx(j, i)] = 0;
+ }
+ }
+ setBnd(0, div);
+ setBnd(0, p);
+
+ for (let k = 0; k < 20; k++) {
+ for (let i = 1; i < ny - 1; i++) {
+ for (let j = 1; j < nx - 1; j++) {
+ p[idx(j, i)] =
+ (div[idx(j, i)] + p[idx(j - 1, i)] + p[idx(j + 1, i)] + p[idx(j, i - 1)] + p[idx(j, i + 1)]) / 4;
+ }
+ }
+ setBnd(0, p);
+ }
+
+ for (let i = 1; i < ny - 1; i++) {
+ for (let j = 1; j < nx - 1; j++) {
+ u[idx(j, i)] -= 0.5 * density * (p[idx(j + 1, i)] - p[idx(j - 1, i)]) / density;
+ v[idx(j, i)] -= 0.5 * density * (p[idx(j, i + 1)] - p[idx(j, i - 1)]) / density;
+ }
+ }
+ setBnd(1, u);
+ setBnd(2, v);
+}
+
+function update(dt) {
+ for (let i = 0; i < nx * ny; i++) {
+ du[i] = u[i];
+ dv[i] = v[i];
+ }
+
+ diffuse(1, u, du, visc, dt);
+ diffuse(2, v, dv, visc, dt);
+
+ project(u, v, p, div);
+
+ advect(1, u, du, u, v, dt);
+ advect(2, v, dv, u, v, dt);
+
+ project(u, v, p, div);
+
+ for (let i = 0; i < nx * ny; i++) {
+ colorGrid[i][0] *= 0.98;
+ colorGrid[i][1] *= 0.98;
+ colorGrid[i][2] *= 0.98;
+ }
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = 1; i < ny - 1; i++) {
+ for (let j = 1; j < nx - 1; j++) {
+ let x = j * density;
+ let y = i * density;
+ let uVal = u[idx(j, i)];
+ let vVal = v[idx(j, i)];
+
+ ctx.fillStyle = `rgb(${colorGrid[idx(j, i)][0]}, ${colorGrid[idx(j, i)][1]}, ${colorGrid[idx(j, i)][2]})`;
+ ctx.fillRect(x - density / 2, y - density / 2, density, density);
+
+ colorGrid[idx(j, i + 1)][0] += uVal * 255;
+ colorGrid[idx(j + 1, i)][1] += vVal * 255;
+
+ if (uVal > 0) {
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + density * uVal, y);
+ ctx.strokeStyle = `rgba(${colorGrid[idx(j, i)][0]}, ${colorGrid[idx(j, i)][1]}, ${colorGrid[idx(j, i)][2]}, 0.5)`;
+ ctx.stroke();
+ }
+ if (vVal > 0) {
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x, y + density * vVal);
+ ctx.strokeStyle = `rgba(${colorGrid[idx(j, i)][0]}, ${colorGrid[idx(j, i)][1]}, ${colorGrid[idx(j, i)][2]}, 0.5)`;
+ ctx.stroke();
+ }
+ }
+ }
+
+ requestAnimationFrame(() => update(dt));
+}
+
+let p = Array(nx * ny).fill(0);
+let div = Array(nx * ny).fill(0);
+
+canvas.addEventListener('mousemove', (e) => {
+ let rect = canvas.getBoundingClientRect();
+ let x = Math.floor((e.clientX - rect.left) / density);
+ let y = Math.floor((e.clientY - rect.top) / density);
+
+ if (x >= 1 && x < nx - 1 && y >= 1 && y < ny - 1) {
+ let color = document.getElementById('colorPicker').value;
+ let [r, g, b] = color.match(/\w\w/g).map(c => parseInt(c, 16));
+
+ u[idx(x, y)] += (e.movementX / density);
+ v[idx(x, y)] += (e.movementY / density);
+
+ colorGrid[idx(x, y)][0] += r;
+ colorGrid[idx(x, y)][1] += g;
+ colorGrid[idx(x, y)][2] += b;
+ }
+});
+
+canvas.addEventListener('mousedown', () => {
+ canvas.style.cursor = 'none';
+});
+
+canvas.addEventListener('mouseup', () => {
+ canvas.style.cursor = 'crosshair';
+});
+
+function clearCanvas() {
+ u.fill(0);
+ v.fill(0);
+ colorGrid.fill([0, 0, 0]);
+}
+
+update(1 / 60);
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/edbe5de71e26/hf-qwen-coder-32b.png b/data/artifacts/edbe5de71e26/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..ad43251
Binary files /dev/null and b/data/artifacts/edbe5de71e26/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/edbe5de71e26/kimi.html b/data/artifacts/edbe5de71e26/kimi.html
new file mode 100644
index 0000000..c39a4c4
--- /dev/null
+++ b/data/artifacts/edbe5de71e26/kimi.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>Fluid Ink Simulation</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ background: #050505;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
+ }
+ canvas {
+ display: block;
+ width: 100vw;
+ height: 100vh;
+ }
+ #controls {
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ z-index: 10;
+ background: rgba(20, 20, 20, 0.9);
+ padding: 15px;
+ border-radius: 8px;
+ border: 1px solid #333;
+ display: flex;
+ gap: 15px;
+ align-items: center;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.5);
+ }
+ button {
+ background: #2a2a2a;
+ color: #fff;
+ border: 1px solid #444;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 14px;
+ transition: background 0.2s;
+ }
+ button:hover {
+ background: #3a3a3a;
+ border-color: #555;
+ }
+ input[type="color"] {
+ border: none;
+ width: 40px;
+ height: 30px;
+ cursor: pointer;
+ background: none;
+ padding: 0;
+ }
+ .label {
+ color: #888;
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ }
+ </style>
+</head>
+<body>
+ <div id="controls">
+ <span class="label">Color</span>
+ <input type="color" id="colorPicker" value="#00ffff">
+ <button id="clearBtn">Clear</button>
+ </div>
+ <canvas id="canvas"></canvas>
+
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d', { alpha: false });
+ const colorPicker = document.getElementById('colorPicker');
+ const clearBtn = document.getElementById('clearBtn');
+
+ // Simulation parameters
+ const N = 160;
+ const SIZE = (N + 2) * (N + 2);
+ const dt = 0.12;
+ const diffusion = 0.00005;
+ const viscosity = 0.00008;
+ const force = 2.0;
+ const source = 80.0;
+
+ // Fluid fields
+ let u = new Float32Array(SIZE);
+ let v = new Float32Array(SIZE);
+ let u_prev = new Float32Array(SIZE);
+ let v_prev = new Float32Array(SIZE);
+ let dens_r = new Float32Array(SIZE);
+ let dens_g = new Float32Array(SIZE);
+ let dens_b = new Float32Array(SIZE);
+ let dens_r_prev = new Float32Array(SIZE);
+ let dens_g_prev = new Float32Array(SIZE);
+ let dens_b_prev = new Float32Array(SIZE);
+
+ // Index macro
+ const IX = (x, y) => x + (N + 2) * y;
+
+ // Setup canvas
+ function resize() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ }
+ window.addEventListener('resize', resize);
+ resize();
+
+ // Solver functions
+ function set_boundary(b, x) {
+ for (let i = 1; i <= N; i++) {
+ if (b === 1) {
+ x[IX(0, i)] = -x[IX(1, i)];
+ x[IX(N+1, i)] = -x[IX(N, i)];
+ } else {
+ x[IX(0, i)] = x[IX(1, i)];
+ x[IX(N+1, i)] = x[IX(N, i)];
+ }
+
+ if (b === 2) {
+ x[IX(i, 0)] = -x[IX(i, 1)];
+ x[IX(i, N+1)] = -x[IX(i, N)];
+ } else {
+ x[IX(i, 0)] = x[IX(i, 1)];
+ x[IX(i, N+1)] = x[IX(i, N)];
+ }
+ }
+
+ x[IX(0, 0)] = 0.5 * (x[IX(1, 0)] + x[IX(0, 1)]);
+ x[IX(0, N+1)] = 0.5 * (x[IX(1, N+1)] + x[IX(0, N)]);
+ x[IX(N+1, 0)] = 0.5 * (x[IX(N, 0)] + x[IX(N+1, 1)]);
+ x[IX(N+1, N+1)] = 0.5 * (x[IX(N, N+1)] + x[IX(N+1, N)]);
+ }
+
+ function lin_solve(b, x, x0, a, c) {
+ const cRecip = 1.0 / c;
+ for (let k = 0; k < 16; k++) {
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ x[IX(i, j)] = (x0[IX(i, j)] + a * (x[IX(i+1, j)] + x[IX(i-1, j)] + x[IX(i, j+1)] + x[IX(i, j-1)])) * cRecip;
+ }
+ }
+ set_boundary(b, x);
+ }
+ }
+
+ function diffuse(b, x, x0, diff, dt) {
+ const a = dt * diff * (N * N);
+ lin_solve(b, x, x0, a, 1 + 4 * a);
+ }
+
+ function project(u, v, p, div) {
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ div[IX(i, j)] = -0.5 * (u[IX(i+1, j)] - u[IX(i-1, j)] + v[IX(i, j+1)] - v[IX(i, j-1)]) / N;
+ p[IX(i, j)] = 0;
+ }
+ }
+ set_boundary(0, div);
+ set_boundary(0, p);
+ lin_solve(0, p, div, 1, 4);
+
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ u[IX(i, j)] -= 0.5 * N * (p[IX(i+1, j)] - p[IX(i-1, j)]);
+ v[IX(i, j)] -= 0.5 * N * (p[IX(i, j+1)] - p[IX(i, j-1)]);
+ }
+ }
+ set_boundary(1, u);
+ set_boundary(2, v);
+ }
+
+ function advect(b, d, d0, u, v, dt) {
+ let i0, j0, i1, j1;
+ let x, y, s0, t0, s1, t1;
+ const dt0 = dt * N;
+
+ for (let j = 1; j <= N; j++) {
+ for (let i = 1; i <= N; i++) {
+ x = i - dt0 * u[IX(i, j)];
+ y = j - dt0 * v[IX(i, j)];
+
+ if (x < 0.5) x = 0.5;
+ if (x > N + 0.5) x = N + 0.5;
+ i0 = Math.floor(x);
+ i1 = i0 + 1;
+
+ if (y < 0.5) y = 0.5;
+ if (y > N + 0.5) y = N + 0.5;
+ j0 = Math.floor(y);
+ j1 = j0 + 1;
+
+ s1 = x - i0;
+ s0 = 1.0 - s1;
+ t1 = y - j0;
+ t0 = 1.0 - t1;
+
+ d[IX(i, j)] = s0 * (t0 * d0[IX(i0, j0)] + t1 * d0[IX(i0, j1)]) +
+ s1 * (t0 * d0[IX(i1, j0)] + t1 * d0[IX(i1, j1)]);
+ }
+ }
+ set_boundary(b, d);
+ }
+
+ function step() {
+ diffuse(1, u_prev, u, viscosity, dt);
+ diffuse(2, v_prev, v, viscosity, dt);
+
+ project(u_prev, v_prev, u, v);
+
+ advect(1, u, u_prev, u_prev, v_prev, dt);
+ advect(2, v, v_prev, u_prev, v_prev, dt);
+
+ project(u, v, u_prev, v_prev);
+
+ diffuse(0, dens_r_prev, dens_r, diffusion, dt);
+ diffuse(0, dens_g_prev, dens_g, diffusion, dt);
+ diffuse(0, dens_b_prev, dens_b, diffusion, dt);
+
+ advect(0, dens_r, dens_r_prev, u, v, dt);
+ advect(0, dens_g, dens_g_prev, u, v, dt);
+ advect(0, dens_b, dens_b_prev, u, v, dt);
+ }
+
+ // Rendering setup
+ const imageData = ctx.createImageData(N+2, N+2);
+ const data = imageData.data;
+ const tempCanvas = document.createElement('canvas');
+ tempCanvas.width = N + 2;
+ tempCanvas.height = N + 2;
+ const tempCtx = tempCanvas.getContext('2d');
+
+ function render() {
+ for (let i = 0; i < SIZE; i++) {
+ const idx = i * 4;
+ const intensity = 2.5; // Brightness multiplier
+ data[idx] = Math.min(255, dens_r[i] * 255 * intensity);
+ data[idx + 1] = Math.min(255, dens_g[i] * 255 * intensity);
+ data[idx + 2] = Math.min(255, dens_b[i] * 255 * intensity);
+ data[idx + 3] = 255;
+ }
+
+ tempCtx.putImageData(imageData, 0, 0);
+ ctx.fillStyle = '#080808';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height);
+ }
+
+ // Input handling
+ let lastX = 0, lastY = 0;
+ let isDrawing = false;
+ let currentColor = { r: 0, g: 1, b: 1 };
+
+ function getGridPos(clientX, clientY) {
+ const rect = canvas.getBoundingClientRect();
+ return {
+ x: Math.floor((clientX - rect.left) / rect.width * (N + 2)),
+ y: Math.floor((clientY - rect.top) / rect.height * (N + 2))
+ };
+ }
+
+ function addInk(x, y, dx, dy) {
+ if (x < 1 || x > N || y < 1 || y > N) return;
+
+ const idx = IX(x, y);
+
+ // Inject velocity (forward + tangential for swirl)
+ u[idx] += dx * force * 3;
+ v[idx] += dy * force * 3;
+ u[idx] -= dy * force * 1.5; // Tangential component
+ v[idx] += dx * force * 1.5;
+
+ // Inject ink with falloff
+ const amount = source;
+ dens_r[idx] += amount * currentColor.r;
+ dens_g[idx] += amount * currentColor.g;
+ dens_b[idx] += amount * currentColor.b;
+
+ // Neighbor smoothing
+ const neighbors = [[1,0], [-1,0], [0,1], [0,-1]];
+ for (let [nx, ny] of neighbors) {
+ const ni = x + nx, nj = y + ny;
+ if (ni >= 1 && ni <= N && nj >= 1 && nj <= N) {
+ const nidx = IX(ni, nj);
+ dens_r[nidx] += amount * currentColor.r * 0.3;
+ dens_g[nidx] += amount * currentColor.g * 0.3;
+ dens_b[nidx] += amount * currentColor.b * 0.3;
+ }
+ }
+ }
+
+ // Mouse events
+ canvas.addEventListener('mousedown', (e) => {
+ isDrawing = true;
+ const pos = getGridPos(e.clientX, e.clientY);
+ lastX = pos.x;
+ lastY = pos.y;
+ addInk(pos.x, pos.y, 0, 0);
+ });
+
+ canvas.addEventListener('mousemove', (e) => {
+ if (!isDrawing) return;
+ const pos = getGridPos(e.clientX, e.clientY);
+ const dx = pos.x - lastX;
+ const dy = pos.y - lastY;
+ addInk(pos.x, pos.y, dx, dy);
+ lastX = pos.x;
+ lastY = pos.y;
+ });
+
+ canvas.addEventListener('mouseup', () => isDrawing = false);
+ canvas.addEventListener('mouseleave', () => isDrawing = false);
+
+ // Touch events
+ canvas.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ isDrawing = true;
+ const touch = e.touches[0];
+ const pos = getGridPos(touch.clientX, touch.clientY);
+ lastX = pos.x;
+ lastY = pos.y;
+ addInk(pos.x, pos.y, 0, 0);
+ });
+
+ canvas.addEventListener('touchmove', (e) => {
+ e.preventDefault();
+ if (!isDrawing) return;
+ const touch = e.touches[0];
+ const pos = getGridPos(touch.clientX, touch.clientY);
+ const dx = pos.x - lastX;
+ const dy = pos.y - lastY;
+ addInk(pos.x, pos.y, dx, dy);
+ lastX = pos.x;
+ lastY = pos.y;
+ });
+
+ canvas.addEventListener('touchend', () => isDrawing = false);
+
+ // UI controls
+ colorPicker.addEventListener('input', (e) => {
+ const hex = e.target.value;
+ currentColor.r = parseInt(hex.substr(1, 2), 16) / 255;
+ currentColor.g = parseInt(hex.substr(3, 2), 16) / 255;
+ currentColor.b = parseInt(hex.substr(5, 2), 16) / 255;
+ });
+
+ clearBtn.addEventListener('click', () => {
+ u.fill(0); v.fill(0);
+ u_prev.fill(0); v_prev.fill(0);
+ dens_r.fill(0); dens_g.fill(0); dens_b.fill(0);
+ dens_r_prev.fill(0); dens_g_prev.fill(0); dens_b_prev.fill(0);
+ });
+
+ // Animation loop
+ function animate() {
+ step();
+ render();
+ requestAnimationFrame(animate);
+ }
+
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/edbe5de71e26/kimi.png b/data/artifacts/edbe5de71e26/kimi.png
new file mode 100644
index 0000000..b4dc0a9
Binary files /dev/null and b/data/artifacts/edbe5de71e26/kimi.png differ
diff --git a/data/artifacts/edbe5de71e26/qwen25-7b.html b/data/artifacts/edbe5de71e26/qwen25-7b.html
new file mode 100644
index 0000000..60a1446
--- /dev/null
+++ b/data/artifacts/edbe5de71e26/qwen25-7b.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Fluid Simulation</title>
+ <style>
+ body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #000; }
+ canvas { cursor: crosshair; border: 2px solid white; }
+ .controls { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); }
+ </style>
+</head>
+<body>
+ <canvas id="fluidCanvas" width="800" height="600"></canvas>
+ <div class="controls">
+ <input type="color" id="colorPicker" value="#ff0000">
+ <button onclick="clearCanvas()">Clear</button>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('fluidCanvas');
+ const ctx = canvas.getContext('2d');
+ const cells = [];
+ let clear = false;
+
+ function init() {
+ for (let y = 0; y < canvas.height; y += 16) {
+ for (let x = 0; x < canvas.width; x += 16) {
+ cells.push({x, y, value: 0});
+ }
+ }
+ }
+
+ function draw() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ const stepSize = 32;
+ for (let i = 0; i < cells.length; i += stepSize) {
+ for (let j = i; j < Math.min(i + stepSize, cells.length); j++) {
+ if (cells[j].value > 0) {
+ ctx.fillStyle = `hsl(${Math.floor(cells[j].x / canvas.width * 360), 100%, 50%)}`;
+ ctx.fillRect(cells[j].x - 8, cells[j].y - 8, 16, 16);
+ }
+ }
+ }
+ }
+
+ function diffuse() {
+ const tmpCells = [...cells];
+ for (let i = 0; i < cells.length; i++) {
+ if (cells[i].value > 0) {
+ let totalValue = cells[i].value;
+ let neighbors = [];
+ for (let dx = -1; dx <= 1; dx++) {
+ for (let dy = -1; dy <= 1; dy++) {
+ const x = cells[i].x + dx * 16;
+ const y = cells[i].y + dy * 16;
+ if (x >= 0 && x < canvas.width && y >= 0 && y < canvas.height) {
+ neighbors.push(cells[y * canvas.width / 16 + Math.floor(x / 16)]);
+ }
+ }
+ }
+ for (const neighbor of neighbors) {
+ totalValue += neighbor.value;
+ }
+ cells[i].value = totalValue / neighbors.length;
+ }
+ }
+ }
+
+ function advect() {
+ const tmpCells = [...cells];
+ for (let i = 0; i < cells.length; i++) {
+ if (cells[i].value > 0) {
+ let dx = Math.floor(cells[i].x / 16);
+ let dy = Math.floor(cells[i].y / 16);
+ const x = tmpCells[dy * canvas.width / 16 + dx].x;
+ const y = tmpCells[dy * canvas.width / 16 + dx].y;
+ cells[i].value *= (cells[i].x - x) / 16 + (cells[i].y - y) / 16;
+ }
+ }
+ }
+
+ function update() {
+ if (!clear) {
+ const color = document.getElementById('colorPicker').value.slice(4);
+ ctx.fillStyle = `#${color}`;
+ for (let i = 0; i < canvas.width * canvas.height / 256; i++) {
+ cells[Math.floor(Math.random() * cells.length)].value += Math.random() * 0.1;
+ }
+ }
+ draw();
+ diffuse();
+ advect();
+ requestAnimationFrame(update);
+ }
+
+ function clearCanvas() {
+ clear = !clear;
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ init();
+ }
+
+ init();
+ update();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/edbe5de71e26/qwen25-7b.png b/data/artifacts/edbe5de71e26/qwen25-7b.png
new file mode 100644
index 0000000..47cc055
Binary files /dev/null and b/data/artifacts/edbe5de71e26/qwen25-7b.png differ
diff --git a/data/artifacts/edbe5de71e26/qwen3-14b.html b/data/artifacts/edbe5de71e26/qwen3-14b.html
new file mode 100644
index 0000000..28df9df
--- /dev/null
+++ b/data/artifacts/edbe5de71e26/qwen3-14b.html
@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Fluid Simulation</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #000;
+ font-family: sans-serif;
+ }
+ canvas {
+ display: block;
+ }
+ #controls {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ background: rgba(0,0,0,0.5);
+ padding: 10px;
+ border-radius: 5px;
+ }
+ #colorPicker {
+ margin: 5px 0;
+ }
+ #clearButton {
+ margin-top: 5px;
+ }
+ </style>
+</head>
+<body>
+ <div id="controls">
+ <label for="colorPicker">Color:</label>
+ <input type="color" id="colorPicker" value="#00ffcc">
+ <button id="clearButton">Clear</button>
+ </div>
+ <canvas id="canvas"></canvas>
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const colorPicker = document.getElementById('colorPicker');
+ const clearButton = document.getElementById('clearButton');
+
+ 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 SIZE = 64;
+ const NUM_CELLS_X = Math.floor(width / SIZE);
+ const NUM_CELLS_Y = Math.floor(height / SIZE);
+
+ const DENSITY = 1;
+ const MAX_DENSITY = 100;
+ const DIFFUSION = 0.19;
+ const ADVECTION = 0.19;
+ const DAMPING = 0.8;
+
+ const density = new Array(NUM_CELLS_X * NUM_CELLS_Y).fill(0);
+ const velocityX = new Array(NUM_CELLS_X * NUM_CELLS_Y).fill(0);
+ const velocityY = new Array(NUM_CELLS_X * NUM_CELLS_Y).fill(0);
+ const densityPrev = new Array(NUM_CELLS_X * NUM_CELLS_Y).fill(0);
+ const velocityXPrev = new Array(NUM_CELLS_X * NUM_CELLS_Y).fill(0);
+ const velocityYPrev = new Array(NUM_CELLS_X * NUM_CELLS_Y).fill(0);
+
+ const mouse = { x: -1, y: -1, down: false };
+ const color = [0, 255, 204];
+
+ function getIndex(x, y) {
+ return y * NUM_CELLS_X + x;
+ }
+
+ function addDensity(x, y, val) {
+ const i = getIndex(x, y);
+ density[i] = Math.min(MAX_DENSITY, density[i] + val);
+ }
+
+ function addVelocity(x, y, vx, vy) {
+ const i = getIndex(x, y);
+ velocityX[i] += vx;
+ velocityY[i] += vy;
+ }
+
+ function advect(field, fieldPrev, vx, vy, dt) {
+ for (let y = 0; y < NUM_CELLS_Y; y++) {
+ for (let x = 0; x < NUM_CELLS_X; x++) {
+ const i = getIndex(x, y);
+ const u = (x - dt * vx[i]) % NUM_CELLS_X;
+ const v = (y - dt * vy[i]) % NUM_CELLS_Y;
+ const uClamped = Math.max(0, Math.min(NUM_CELLS_X - 1, u));
+ const vClamped = Math.max(0, Math.min(NUM_CELLS_Y - 1, v));
+ field[i] = fieldPrev[getIndex(Math.floor(uClamped), Math.floor(vClamped))];
+ }
+ }
+ }
+
+ function diffuse(field, fieldPrev, dt, diff) {
+ for (let y = 0; y < NUM_CELLS_Y; y++) {
+ for (let x = 0; x < NUM_CELLS_X; x++) {
+ fieldPrev[getIndex(x, y)] = field[getIndex(x, y)];
+ }
+ }
+ for (let iter = 0; iter < 20; iter++) {
+ for (let y = 1; y < NUM_CELLS_Y - 1; y++) {
+ for (let x = 1; x < NUM_CELLS_X - 1; x++) {
+ const i = getIndex(x, y);
+ field[i] = (fieldPrev[i] + diff * (fieldPrev[getIndex(x + 1, y)] + fieldPrev[getIndex(x - 1, y)] + fieldPrev[getIndex(x, y + 1)] + fieldPrev[getIndex(x, y - 1)])) / (1 + 4 * diff);
+ }
+ }
+ }
+ }
+
+ function step() {
+ // Advect density
+ advect(density, densityPrev, velocityX, velocityY, ADVECTION);
+ // Advect velocity
+ advect(velocityX, velocityXPrev, velocityX, velocityY, ADVECTION);
+ advect(velocityY, velocityYPrev, velocityX, velocityY, ADVECTION);
+
+ // Diffuse density
+ diffuse(density, densityPrev, DIFFUSION, DIFFUSION);
+ diffuse(velocityX, velocityXPrev, DIFFUSION, DIFFUSION);
+ diffuse(velocityY, velocityYPrev, DIFFUSION, DIFFUSION);
+
+ // Damp velocity
+ for (let i = 0; i < density.length; i++) {
+ velocityX[i] *= DAMPING;
+ velocityY[i] *= DAMPING;
+ }
+
+ // Copy to previous for next step
+ for (let i = 0; i < density.length; i++) {
+ densityPrev[i] = density[i];
+ velocityXPrev[i] = velocityX[i];
+ velocityYPrev[i] = velocityY[i];
+ }
+ }
+
+ function draw() {
+ ctx.clearRect(0, 0, width, height);
+ for (let y = 0; y < NUM_CELLS_Y; y++) {
+ for (let x = 0; x < NUM_CELLS_X; x++) {
+ const i = getIndex(x, y);
+ const d = density[i];
+ if (d > 0) {
+ const value = Math.min(1, d / MAX_DENSITY);
+ const r = Math.floor(color[0] * value);
+ const g = Math.floor(color[1] * value);
+ const b = Math.floor(color[2] * value);
+ ctx.fillStyle = `rgb(${r},${g},${b})`;
+ ctx.fillRect(x * SIZE, y * SIZE, SIZE, SIZE);
+ }
+ }
+ }
+ }
+
+ function animate() {
+ step();
+ draw();
+ requestAnimationFrame(animate);
+ }
+
+ canvas.addEventListener('mousedown', (e) => {
+ mouse.x = e.offsetX;
+ mouse.y = e.offsetY;
+ mouse.down = true;
+ });
+
+ canvas.addEventListener('mousemove', (e) => {
+ if (mouse.down) {
+ mouse.x = e.offsetX;
+ mouse.y = e.offsetY;
+ const x = Math.floor(mouse.x / SIZE);
+ const y = Math.floor(mouse.y / SIZE);
+ addDensity(x, y, 10);
+ addVelocity(x, y, 0.1, 0.1);
+ }
+ });
+
+ canvas.addEventListener('mouseup', () => {
+ mouse.down = false;
+ });
+
+ clearButton.addEventListener('click', () => {
+ for (let i = 0; i < density.length; i++) {
+ density[i] = 0;
+ velocityX[i] = 0;
+ velocityY[i] = 0;
+ }
+ });
+
+ colorPicker.addEventListener('input', (e) => {
+ const c = e.target.value;
+ color[0] = parseInt(c.slice(1, 3), 16);
+ color[1] = parseInt(c.slice(3, 5), 16);
+ color[2] = parseInt(c.slice(5, 7), 16);
+ });
+
+ animate();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/edbe5de71e26/qwen3-14b.png b/data/artifacts/edbe5de71e26/qwen3-14b.png
new file mode 100644
index 0000000..5c6c5e7
Binary files /dev/null and b/data/artifacts/edbe5de71e26/qwen3-14b.png differ
diff --git a/data/artifacts/f1756a933e20/claude-code.html b/data/artifacts/f1756a933e20/claude-code.html
new file mode 100644
index 0000000..d0161d5
--- /dev/null
+++ b/data/artifacts/f1756a933e20/claude-code.html
@@ -0,0 +1,337 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>ABRAMS-86 BIOS</title>
+<style>
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+
+ html, body {
+ height: 100%;
+ background: #0a0a0a;
+ overflow: hidden;
+ }
+
+ .crt-frame {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background: radial-gradient(ellipse at center, #061306 0%, #030a03 70%, #010401 100%);
+ border-radius: 18px;
+ overflow: hidden;
+ cursor: text;
+ }
+
+ #screen {
+ position: absolute;
+ inset: 0;
+ padding: 24px 28px;
+ overflow-y: auto;
+ font-family: "Menlo", "Consolas", "Courier New", monospace;
+ font-size: 15px;
+ line-height: 1.45;
+ color: #33ff66;
+ text-shadow: 0 0 2px rgba(51,255,102,.9), 0 0 8px rgba(51,255,102,.45), 0 0 20px rgba(51,255,102,.15);
+ white-space: pre-wrap;
+ word-break: break-word;
+ animation: flicker 4s infinite;
+ scrollbar-width: none;
+ }
+ #screen::-webkit-scrollbar { display: none; }
+
+ #screen .dim { color: #1e9944; }
+ #screen .bold { color: #7dffa0; font-weight: bold; }
+ #screen .ok { color: #a8ffbe; }
+
+ /* static scanlines */
+ .scanlines {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ background: repeating-linear-gradient(
+ to bottom,
+ rgba(0,0,0,0) 0px,
+ rgba(0,0,0,0) 2px,
+ rgba(0,0,0,.28) 3px,
+ rgba(0,0,0,0) 4px
+ );
+ mix-blend-mode: multiply;
+ z-index: 3;
+ }
+
+ /* slow rolling bright band */
+ .rollbar {
+ position: absolute;
+ left: 0; right: 0;
+ height: 90px;
+ background: linear-gradient(to bottom, rgba(51,255,102,0) 0%, rgba(51,255,102,.05) 50%, rgba(51,255,102,0) 100%);
+ pointer-events: none;
+ z-index: 4;
+ animation: roll 7s linear infinite;
+ }
+ @keyframes roll {
+ 0% { top: -12%; }
+ 100% { top: 112%; }
+ }
+
+ /* vignette + glass curvature illusion */
+ .vignette {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ z-index: 5;
+ background: radial-gradient(ellipse at center, rgba(0,0,0,0) 55%, rgba(0,0,0,.45) 92%, rgba(0,0,0,.8) 100%);
+ box-shadow: inset 0 0 120px rgba(0,0,0,.6);
+ border-radius: 18px;
+ }
+ .glass {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ z-index: 6;
+ background: linear-gradient(115deg, rgba(255,255,255,.045) 0%, rgba(255,255,255,0) 30%);
+ border-radius: 18px;
+ }
+
+ @keyframes flicker {
+ 0% { opacity: .972; }
+ 3% { opacity: .94; }
+ 5% { opacity: .985; }
+ 9% { opacity: .96; }
+ 11% { opacity: 1; }
+ 24% { opacity: .97; }
+ 27% { opacity: .93; }
+ 29% { opacity: .99; }
+ 46% { opacity: .965; }
+ 52% { opacity: 1; }
+ 63% { opacity: .95; }
+ 66% { opacity: .98; }
+ 78% { opacity: .96; }
+ 83% { opacity: 1; }
+ 91% { opacity: .955; }
+ 100% { opacity: .975; }
+ }
+
+ #cursor {
+ display: inline-block;
+ width: .62em;
+ height: 1.05em;
+ background: #33ff66;
+ box-shadow: 0 0 6px rgba(51,255,102,.8), 0 0 16px rgba(51,255,102,.4);
+ vertical-align: text-bottom;
+ animation: blink 1.06s steps(1) infinite;
+ }
+ @keyframes blink {
+ 0%, 55% { opacity: 1; }
+ 56%,100% { opacity: 0; }
+ }
+
+ /* occasional horizontal jitter on the whole tube */
+ .crt-frame { animation: jitter 9s infinite; }
+ @keyframes jitter {
+ 0%, 96.8%, 100% { transform: none; }
+ 97% { transform: translateX(1px) skewX(.15deg); }
+ 97.4% { transform: translateX(-1px); }
+ 97.8% { transform: none; }
+ }
+</style>
+</head>
+<body>
+<div class="crt-frame" id="frame">
+ <div id="screen"><span id="out"></span><span id="typed"></span><span id="cursor"></span></div>
+ <div class="scanlines"></div>
+ <div class="rollbar"></div>
+ <div class="vignette"></div>
+ <div class="glass"></div>
+</div>
+
+<script>
+(function () {
+ var out = document.getElementById('out');
+ var typed = document.getElementById('typed');
+ var screen = document.getElementById('screen');
+
+ var mode = 'boot'; // boot -> prompt
+ var buffer = ''; // current command line input
+ var PROMPT = 'guest@abrams-86:~$ ';
+
+ function scrollDown() { screen.scrollTop = screen.scrollHeight; }
+
+ function append(html) {
+ out.innerHTML += html;
+ scrollDown();
+ }
+
+ /* ---------- boot sequence ---------- */
+
+ var bootLines = [
+ { t: '<span class="bold">ABRAMS-86 ROM BIOS v3.11</span>', d: 350 },
+ { t: 'Copyright (C) 1986-1994 Abrams Micro Systems, Inc.', d: 500 },
+ { t: '', d: 250 },
+ { t: 'CPU: i486DX2-66 @ 66 MHz .................. <span class="ok">OK</span>', d: 240 },
+ { t: 'Coprocessor ............................... <span class="ok">DETECTED</span>', d: 220 },
+ { t: 'Memory Test: 640K base .................... <span class="ok">PASS</span>', d: 160 },
+ { t: 'Memory Test: 16384K extended .............. <span class="ok">PASS</span>', d: 420 },
+ { t: '', d: 180 },
+ { t: 'Detecting IDE drives ...', d: 600 },
+ { t: ' Primary Master : QUANTUM LPS540A 540 MB', d: 260 },
+ { t: ' Primary Slave : <span class="dim">none</span>', d: 200 },
+ { t: ' Secondary Master: CR-563 CD-ROM', d: 300 },
+ { t: 'Floppy A: 1.44M 3.5" ..................... <span class="ok">OK</span>', d: 260 },
+ { t: '', d: 200 },
+ { t: 'Initializing video (VGA 720x400 text) ..... <span class="ok">OK</span>', d: 240 },
+ { t: 'Keyboard .................................. <span class="ok">OK</span>', d: 220 },
+ { t: 'Serial ports: COM1 COM2', d: 200 },
+ { t: 'Parallel ports: LPT1', d: 320 },
+ { t: '', d: 240 },
+ { t: 'Booting from C: ...', d: 700 },
+ { t: 'Loading ABRAMS-DOS kernel ................. <span class="ok">DONE</span>', d: 380 },
+ { t: 'HIMEM is testing extended memory .......... <span class="ok">DONE</span>', d: 320 },
+ { t: 'Mounting file systems ..................... <span class="ok">DONE</span>', d: 420 },
+ { t: '', d: 260 },
+ { t: '<span class="bold">ABRAMS-DOS 6.22 — phosphor terminal ready.</span>', d: 300 },
+ { t: 'Type <span class="bold">help</span> for a list of commands.', d: 240 },
+ { t: '', d: 150 }
+ ];
+
+ function typeLine(line, done) {
+ // type the plain-text segments char by char, keep tags intact
+ var parts = line.split(/(<[^>]+>)/g).filter(function (p) { return p !== ''; });
+ var pi = 0, ci = 0;
+ var speed = 6 + Math.random() * 8;
+
+ function step() {
+ if (pi >= parts.length) {
+ append('\n');
+ done();
+ return;
+ }
+ var part = parts[pi];
+ if (part.charAt(0) === '<') { // whole tag at once
+ append(part);
+ pi++; ci = 0;
+ step();
+ return;
+ }
+ if (ci < part.length) {
+ append(part.charAt(ci) === '&' ? '&' : part.charAt(ci));
+ ci++;
+ setTimeout(step, speed);
+ } else {
+ pi++; ci = 0;
+ step();
+ }
+ }
+ if (line === '') { append('\n'); done(); return; }
+ step();
+ }
+
+ function runBoot(i) {
+ if (i >= bootLines.length) {
+ startPrompt();
+ return;
+ }
+ typeLine(bootLines[i].t, function () {
+ setTimeout(function () { runBoot(i + 1); }, bootLines[i].d);
+ });
+ }
+
+ /* ---------- interactive prompt ---------- */
+
+ function esc(s) {
+ return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
+ }
+
+ function showPrompt() {
+ append('<span class="dim">' + esc(PROMPT) + '</span>');
+ buffer = '';
+ typed.textContent = '';
+ }
+
+ function startPrompt() {
+ mode = 'prompt';
+ showPrompt();
+ }
+
+ var commands = {
+ help: function () {
+ return [
+ 'Available commands:',
+ ' <span class="bold">help</span> — show this list',
+ ' <span class="bold">date</span> — display current system date/time',
+ ' <span class="bold">clear</span> — clear the screen',
+ ' <span class="bold">about</span> — system information',
+ ' <span class="bold">echo</span> — echo <text> back to the terminal'
+ ].join('\n');
+ },
+ date: function () {
+ return esc(new Date().toString());
+ },
+ clear: function () {
+ out.innerHTML = '';
+ return null;
+ },
+ about: function () {
+ return 'ABRAMS-86 phosphor terminal · 720x400 @ 70Hz · P1 green phosphor\nNo network. No mouse. No mercy.';
+ },
+ echo: function (args) {
+ return esc(args.join(' '));
+ }
+ };
+
+ function execute(raw) {
+ var line = raw.trim();
+ // echo the entered command permanently into the log
+ append(esc(raw) + '\n');
+ typed.textContent = '';
+
+ if (line !== '') {
+ var argv = line.split(/\s+/);
+ var cmd = argv[0].toLowerCase();
+ var fn = commands[cmd];
+ if (fn) {
+ var result = fn(argv.slice(1));
+ if (result !== null && result !== '') append(result + '\n');
+ } else {
+ append('Bad command or file name: <span class="bold">' + esc(cmd) + '</span>\nType <span class="bold">help</span> for available commands.\n');
+ }
+ }
+ showPrompt();
+ }
+
+ document.addEventListener('keydown', function (e) {
+ if (mode !== 'prompt') return;
+ if (e.metaKey || e.ctrlKey || e.altKey) return;
+
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ execute(buffer);
+ } else if (e.key === 'Backspace') {
+ e.preventDefault();
+ buffer = buffer.slice(0, -1);
+ typed.textContent = buffer;
+ scrollDown();
+ } else if (e.key.length === 1) {
+ e.preventDefault();
+ if (buffer.length < 200) {
+ buffer += e.key;
+ typed.textContent = buffer;
+ scrollDown();
+ }
+ }
+ });
+
+ // keep focus-friendly inside an iframe
+ document.getElementById('frame').addEventListener('mousedown', function (e) {
+ e.preventDefault();
+ window.focus();
+ });
+ window.focus();
+
+ // power-on: brief warm-up before the boot log starts typing
+ setTimeout(function () { runBoot(0); }, 450);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f1756a933e20/claude-code.png b/data/artifacts/f1756a933e20/claude-code.png
new file mode 100644
index 0000000..bd11ac4
Binary files /dev/null and b/data/artifacts/f1756a933e20/claude-code.png differ
diff --git a/data/artifacts/f1756a933e20/gemma3-12b.html b/data/artifacts/f1756a933e20/gemma3-12b.html
new file mode 100644
index 0000000..4b794b1
--- /dev/null
+++ b/data/artifacts/f1756a933e20/gemma3-12b.html
@@ -0,0 +1,153 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Retro Terminal</title>
+ <style>
+ body {
+ background-color: #000;
+ color: #0f0;
+ font-family: monospace;
+ line-height: 1.2;
+ margin: 0;
+ overflow: hidden; /* Prevent scrollbars */
+ }
+
+ #terminal {
+ font-size: 16px;
+ padding: 1em;
+ white-space: pre; /* Preserve whitespace */
+ cursor: text;
+ }
+
+ .scanline {
+ background-color: rgba(0, 0, 0, 0.1);
+ height: 2px;
+ }
+
+ #prompt {
+ color: #0f0;
+ }
+ </style>
+</head>
+<body>
+
+<div id="terminal"></div>
+
+<script>
+ const terminal = document.getElementById('terminal');
+ let lines = [];
+ let cursorX = 0;
+ let cursorY = 0;
+ let flickerInterval;
+ let promptDisplayed = false;
+
+ function typeText(text, delay = 50) {
+ for (let i = 0; i < text.length; i++) {
+ setTimeout(() => {
+ terminal.innerHTML += text[i];
+ cursorX++;
+ lines.push(text[i]);
+
+ if (cursorX > 80) {
+ cursorX = 0;
+ cursorY++;
+ scrollDown();
+ }
+ }, i * delay);
+ }
+ }
+
+ function scrollDown() {
+ if (lines.length > 100) {
+ lines.shift();
+ terminal.innerHTML = lines.join('');
+ cursorY--;
+ }
+ }
+
+
+ function flicker() {
+ const terminalDisplay = document.getElementById('terminal');
+ if (Math.random() < 0.2) { // Adjust frequency as needed
+ terminalDisplay.style.opacity = '0.1';
+ setTimeout(() => { terminalDisplay.style.opacity = '1' }, 5);
+ }
+ }
+
+ function addScanline() {
+ const scanlineDiv = document.createElement('div');
+ scanlineDiv.classList.add('scanline');
+ terminal.appendChild(scanlineDiv);
+ }
+
+
+ function bootSequence() {
+ typeText("SYSTEM BOOTING...", 20);
+ setTimeout(() => { typeText("\nInitializing Hardware...", 20); }, 500);
+ setTimeout(() => { typeText("\nLoading Kernel...", 20); }, 1000);
+ setTimeout(() => { typeText("\nMemory Check: OK", 20); }, 1500);
+ setTimeout(() => { typeText("\nSystem Ready.", 20); }, 2000);
+
+ // Add a few scanlines during boot
+ for (let i = 0; i < 5; i++) {
+ setTimeout(addScanline, (i + 1) * 300 );
+ }
+
+
+ setTimeout(() => { promptDisplayed = true; displayPrompt(); }, 2500);
+
+ flickerInterval = setInterval(flicker, 100);
+ }
+
+ function displayPrompt() {
+ typeText("\n> ");
+ }
+
+
+
+ function executeCommand(command) {
+ if (command === "help") {
+ typeText("\nAvailable commands: help, date, clear\n> ");
+ } else if (command === "date") {
+ const now = new Date();
+ typeText(`\nDate: ${now.toLocaleDateString()} ${now.toLocaleTimeString()}\n> `);
+ } else if (command === "clear") {
+ lines = [];
+ terminal.innerHTML = "";
+ cursorX = 0;
+ cursorY = 0;
+ typeText("\nClearing screen...\n> ");
+ } else {
+ typeText(`\nUnknown command: ${command}\n> `);
+ }
+ }
+
+
+ function handleInput(event) {
+ if (event.key === "Enter") {
+ const input = terminal.innerHTML.substring(terminal.innerHTML.lastIndexOf("> ") + 2).trim();
+ executeCommand(input);
+ terminal.innerHTML += "\n> "; // Add new prompt after command execution
+ cursorX = 0;
+ } else{
+ if (promptDisplayed) {
+ terminal.innerHTML += event.key;
+ cursorX++;
+
+ if (cursorX > 80) {
+ cursorX = 0;
+ }
+ }
+ }
+ }
+
+
+ document.addEventListener('keydown', handleInput);
+
+ bootSequence();
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f1756a933e20/gemma3-12b.png b/data/artifacts/f1756a933e20/gemma3-12b.png
new file mode 100644
index 0000000..ecb98a1
Binary files /dev/null and b/data/artifacts/f1756a933e20/gemma3-12b.png differ
diff --git a/data/artifacts/f1756a933e20/gpt.html b/data/artifacts/f1756a933e20/gpt.html
new file mode 100644
index 0000000..a6aff9c
--- /dev/null
+++ b/data/artifacts/f1756a933e20/gpt.html
@@ -0,0 +1,344 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<title>CRT Boot Sequence</title>
+<style>
+ html, body {
+ background: #000;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ color: #0f0;
+ font-family: "Courier New", monospace;
+ }
+ body {
+ display: flex;
+ align-items: stretch;
+ justify-content: center;
+ }
+ .crt-wrapper {
+ position: relative;
+ flex: 1;
+ max-width: 900px;
+ margin: 0 auto;
+ background: radial-gradient(circle at center, #022 0%, #000 65%, #000 100%);
+ box-shadow:
+ 0 0 25px rgba(0, 255, 0, 0.3),
+ inset 0 0 40px rgba(0, 0, 0, 0.9);
+ border: 4px solid #030;
+ border-radius: 8px;
+ overflow: hidden;
+ }
+ .screen {
+ position: absolute;
+ inset: 10px 14px;
+ padding: 8px 12px;
+ color: #9f9;
+ font-size: 14px;
+ line-height: 1.3;
+ text-shadow:
+ 0 0 2px #0f0,
+ 0 0 6px rgba(0, 255, 0, 0.7);
+ overflow: hidden;
+ white-space: pre-wrap;
+ word-break: break-word;
+ }
+ /* Scanlines */
+ .screen::before {
+ content: "";
+ pointer-events: none;
+ position: absolute;
+ inset: 0;
+ background: repeating-linear-gradient(
+ to bottom,
+ rgba(0, 0, 0, 0.0) 0px,
+ rgba(0, 0, 0, 0.08) 2px,
+ rgba(0, 0, 0, 0.25) 3px
+ );
+ mix-blend-mode: multiply;
+ opacity: 0.9;
+ z-index: 5;
+ }
+ /* Subtle flicker and glow */
+ .crt-wrapper::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ background:
+ radial-gradient(circle at 50% -20%, rgba(255, 255, 255, 0.08), transparent 60%),
+ radial-gradient(circle at 50% 120%, rgba(0, 255, 0, 0.15), transparent 65%);
+ mix-blend-mode: screen;
+ opacity: 0.4;
+ animation: flicker 0.15s infinite;
+ }
+ @keyframes flicker {
+ 0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% {
+ opacity: 0.35;
+ }
+ 20%, 24%, 55% {
+ opacity: 0.5;
+ }
+ }
+ /* Barrel distortion (subtle) */
+ .screen-inner {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ transform: perspective(800px) translateZ(0) scale(1.02);
+ }
+ #output {
+ position: absolute;
+ inset: 0;
+ overflow-y: auto;
+ padding-right: 10px;
+ }
+ /* Hide native scrollbar but keep scrollability */
+ #output::-webkit-scrollbar {
+ width: 0px;
+ height: 0px;
+ }
+ /* Cursor */
+ .cursor {
+ display: inline-block;
+ width: 8px;
+ margin-left: 1px;
+ background: #9f9;
+ box-shadow: 0 0 4px #0f0;
+ animation: cursor-blink 0.8s steps(1) infinite;
+ }
+ @keyframes cursor-blink {
+ 0%, 50% { opacity: 1; }
+ 51%, 100% { opacity: 0; }
+ }
+ .hidden {
+ display: none;
+ }
+ .dim {
+ opacity: 0.6;
+ }
+ .bright {
+ opacity: 0.95;
+ }
+ .prompt-line {
+ display: inline;
+ }
+ .input-text {
+ display: inline;
+ }
+ .error {
+ color: #f66;
+ }
+ .ok {
+ color: #9f9;
+ }
+ .title {
+ color: #bfb;
+ }
+ .muted {
+ color: #6a6;
+ }
+</style>
+</head>
+<body>
+<div class="crt-wrapper">
+ <div class="screen">
+ <div class="screen-inner">
+ <div id="output"></div>
+ <span id="cursor" class="cursor"></span>
+ </div>
+ </div>
+</div>
+
+<script>
+(function() {
+ const outputEl = document.getElementById('output');
+ const cursorEl = document.getElementById('cursor');
+
+ const bootLines = [
+ { t: 400, text: "PHOENIX-OS v3.9.12 (c) 1984-1999 Phoenix Retro Systems, Inc.\n" },
+ { t: 400, text: "BIOS Date: 07/23/86 16:32:09 Ver: R3.9-CRT\n\n" },
+ { t: 320, text: "Performing memory test... " },
+ { t: 90, text: "OK\n" },
+ { t: 260, text: "Detecting peripherals...\n" },
+ { t: 180, text: " [0:0:0] FLOPPY DRIVE 5.25\" 360KB ... " },
+ { t: 90, text: "FOUND\n" },
+ { t: 180, text: " [0:1:0] FLOPPY DRIVE 3.5\" 1.44MB ... " },
+ { t: 90, text: "FOUND\n" },
+ { t: 180, text: " [1:0:0] HDD ST-506 20MB ... " },
+ { t: 90, text: "FOUND\n" },
+ { t: 240, text: " [3:0:0] SERIAL MOUSE ... " },
+ { t: 90, text: "FOUND\n" },
+ { t: 260, text: "\nInitializing video adapter: MDA/Green-Phosphor\n" },
+ { t: 260, text: "Loading kernel image: /boot/phoenix.krn" },
+ { t: 70, text: "." },
+ { t: 70, text: "." },
+ { t: 70, text: "." },
+ { t: 200, text: " done\n" },
+ { t: 200, text: "Verifying kernel checksums... " },
+ { t: 160, text: "[OK]\n" },
+ { t: 220, text: "Mounting root filesystem (ext2) on /dev/hda1 ... " },
+ { t: 200, text: "done\n" },
+ { t: 260, text: "Starting system services:\n" },
+ { t: 160, text: " [OK] system logger\n" },
+ { t: 140, text: " [OK] job scheduler\n" },
+ { t: 140, text: " [OK] network loopback\n" },
+ { t: 160, text: " [OK] random number generator\n" },
+ { t: 240, text: "\nProbing network interfaces...\n" },
+ { t: 180, text: " ne0: 10BASE2 Ethernet (intr 5, base 0x300) ... " },
+ { t: 120, text: "UP\n" },
+ { t: 260, text: "\nSystem time source: CMOS RTC\n" },
+ { t: 240, text: "Entropy pool: seeded from disk and keyboard\n" },
+ { t: 260, text: "\nLaunching /sbin/init...\n" },
+ { t: 350, text: "Entering multi-user text mode.\n" },
+ { t: 350, text: "\nphoenix login: root\n" },
+ { t: 260, text: "Last login: Thu Jul 23 16:32:09 on tty0\n\n" },
+ { t: 260, text: "Welcome to \x1b[1mPHOENIX-OS\x1b[0m retro console.\n" },
+ { t: 260, text: "Type 'help' for a list of built-in commands.\n\n" }
+ ];
+
+ let bootIndex = 0;
+ let bootDone = false;
+
+ const PROMPT = "root@phoenix:~# ";
+ let currentInput = "";
+ let inputActive = false;
+
+ function appendText(text) {
+ // Replace ANSI bold (very basic) with span
+ text = text.replace(/\x1b\[1m/g, '<span class="bright">')
+ .replace(/\x1b\[0m/g, '</span>');
+ outputEl.insertAdjacentHTML('beforeend', text);
+ outputEl.scrollTop = outputEl.scrollHeight;
+ }
+
+ function printPrompt() {
+ appendText('<span class="prompt-line">' + PROMPT + '</span><span class="input-text"></span>');
+ }
+
+ function updatePromptInput() {
+ const inputSpan = outputEl.querySelector('.input-text:last-of-type');
+ if (inputSpan) {
+ inputSpan.textContent = currentInput;
+ }
+ }
+
+ function placeCursor() {
+ const rect = outputEl.getBoundingClientRect();
+ const selection = window.getSelection();
+ let range = document.createRange();
+ let lastNode = outputEl.lastChild;
+
+ if (!lastNode) {
+ outputEl.appendChild(document.createTextNode(""));
+ lastNode = outputEl.lastChild;
+ }
+
+ // If last is element, go deep to its last text
+ while (lastNode && lastNode.lastChild) {
+ lastNode = lastNode.lastChild;
+ }
+ try {
+ range.setStart(lastNode, lastNode.nodeType === 3 ? lastNode.textContent.length : 0);
+ range.collapse(true);
+ const tempSpan = document.createElement('span');
+ range.insertNode(tempSpan);
+ const r = tempSpan.getBoundingClientRect();
+ tempSpan.parentNode.removeChild(tempSpan);
+
+ cursorEl.style.top = (r.bottom - rect.top - 12) + "px";
+ cursorEl.style.left = (r.right - rect.left + 1) + "px";
+ } catch (e) {
+ cursorEl.style.top = (rect.bottom - rect.top - 20) + "px";
+ cursorEl.style.left = (rect.left + 10) + "px";
+ }
+ }
+
+ function processCommand(cmd) {
+ const parts = cmd.trim().split(/\s+/);
+ const main = parts[0].toLowerCase();
+ let output = "";
+
+ if (!main) {
+ return "\n";
+ }
+
+ if (main === "help") {
+ output += "\nAvailable commands:\n";
+ output += " help - show this help message\n";
+ output += " date - display current system time\n";
+ output += " clear - clear the screen\n\n";
+ return output;
+ } else if (main === "date") {
+ const now = new Date();
+ output += "\n" + now.toString() + "\n\n";
+ return output;
+ } else if (main === "clear") {
+ outputEl.textContent = "";
+ return "";
+ } else {
+ output += "\nUnknown command: " + main + "\nType 'help' for a list of commands.\n\n";
+ return output;
+ }
+ }
+
+ function handleKeydown(e) {
+ if (!bootDone || !inputActive) return;
+
+ if (e.key === "Backspace") {
+ e.preventDefault();
+ currentInput = currentInput.slice(0, -1);
+ updatePromptInput();
+ placeCursor();
+ } else if (e.key === "Enter") {
+ e.preventDefault();
+ appendText("\n");
+ const cmd = currentInput;
+ currentInput = "";
+ const result = processCommand(cmd);
+ if (result !== "") {
+ appendText(result);
+ }
+ printPrompt();
+ updatePromptInput();
+ placeCursor();
+ } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
+ e.preventDefault();
+ currentInput += e.key;
+ updatePromptInput();
+ placeCursor();
+ } else if (e.key === "Tab") {
+ e.preventDefault();
+ }
+ }
+
+ function runBootSequence() {
+ if (bootIndex >= bootLines.length) {
+ bootDone = true;
+ inputActive = true;
+ printPrompt();
+ updatePromptInput();
+ placeCursor();
+ return;
+ }
+ const line = bootLines[bootIndex++];
+ appendText(line.text);
+ placeCursor();
+ setTimeout(runBootSequence, line.t);
+ }
+
+ window.addEventListener('resize', function() {
+ placeCursor();
+ });
+
+ window.addEventListener('keydown', handleKeydown);
+
+ // Start immediately
+ cursorEl.style.display = "inline-block";
+ runBootSequence();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f1756a933e20/gpt.png b/data/artifacts/f1756a933e20/gpt.png
new file mode 100644
index 0000000..5fdad4c
Binary files /dev/null and b/data/artifacts/f1756a933e20/gpt.png differ
diff --git a/data/artifacts/f1756a933e20/grok.html b/data/artifacts/f1756a933e20/grok.html
new file mode 100644
index 0000000..b897479
--- /dev/null
+++ b/data/artifacts/f1756a933e20/grok.html
@@ -0,0 +1,376 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>CRT Terminal</title>
+<style>
+ @import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
+</style>
+<style>
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+ html, body {
+ width: 100%; height: 100%;
+ overflow: hidden;
+ background: #000;
+ }
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-family: "Courier New", Courier, monospace;
+ }
+ .crt {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background: #0a0f0a;
+ overflow: hidden;
+ box-shadow: inset 0 0 80px rgba(0, 40, 0, 0.9),
+ inset 0 0 200px rgba(0, 20, 0, 0.6);
+ }
+ .crt::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ rgba(0, 0, 0, 0) 50%,
+ rgba(0, 0, 0, 0.25) 50%
+ );
+ background-size: 100% 4px;
+ pointer-events: none;
+ z-index: 10;
+ }
+ .crt::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(
+ ellipse at center,
+ transparent 50%,
+ rgba(0, 0, 0, 0.4) 100%
+ );
+ pointer-events: none;
+ z-index: 11;
+ }
+ .flicker {
+ position: absolute;
+ inset: 0;
+ background: rgba(18, 16, 16, 0.1);
+ opacity: 0;
+ z-index: 12;
+ pointer-events: none;
+ animation: flicker 0.15s infinite;
+ }
+ @keyframes flicker {
+ 0% { opacity: 0.02; }
+ 5% { opacity: 0.06; }
+ 10% { opacity: 0.01; }
+ 15% { opacity: 0.04; }
+ 20% { opacity: 0; }
+ 50% { opacity: 0.03; }
+ 80% { opacity: 0.01; }
+ 100% { opacity: 0.05; }
+ }
+ .screen {
+ position: relative;
+ z-index: 5;
+ width: 100%;
+ height: 100%;
+ padding: 24px 28px;
+ overflow-y: auto;
+ color: #33ff66;
+ font-size: 18px;
+ line-height: 1.45;
+ text-shadow: 0 0 5px #33ff66, 0 0 10px rgba(51, 255, 102, 0.4);
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ }
+ .screen::-webkit-scrollbar { width: 8px; }
+ .screen::-webkit-scrollbar-track { background: #0a0f0a; }
+ .screen::-webkit-scrollbar-thumb { background: #1a5a2a; }
+ .cursor {
+ display: inline-block;
+ width: 10px;
+ height: 18px;
+ background: #33ff66;
+ box-shadow: 0 0 6px #33ff66;
+ animation: blink 1s step-end infinite;
+ vertical-align: text-bottom;
+ margin-left: 1px;
+ }
+ @keyframes blink {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0; }
+ }
+ .line { min-height: 1.45em; }
+ .prompt-line {
+ display: flex;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ }
+ .prompt-prefix {
+ color: #33ff66;
+ flex-shrink: 0;
+ }
+ #cmdInput {
+ background: transparent;
+ border: none;
+ outline: none;
+ color: #33ff66;
+ font-family: inherit;
+ font-size: inherit;
+ text-shadow: 0 0 5px #33ff66, 0 0 10px rgba(51, 255, 102, 0.4);
+ caret-color: transparent;
+ flex: 1;
+ min-width: 50px;
+ padding: 0;
+ margin: 0;
+ }
+ .hidden { display: none !important; }
+ .dim { opacity: 0.7; }
+ .bright { color: #66ff99; text-shadow: 0 0 8px #66ff99, 0 0 14px rgba(102, 255, 153, 0.5); }
+</style>
+</head>
+<body>
+<div class="crt">
+ <div class="flicker"></div>
+ <div class="screen" id="screen"></div>
+</div>
+<script>
+(function () {
+ var screen = document.getElementById("screen");
+ var bootDone = false;
+ var busy = true;
+ var inputEl = null;
+ var promptPrefix = "user@crt:~$ ";
+
+ var bootLines = [
+ { t: "COMMODORE CRT BIOS v2.41", d: 40 },
+ { t: "Copyright (C) 1982-2024 Retro Systems Inc.", d: 30 },
+ { t: "", d: 200 },
+ { t: "Memory Test ........... 640K OK", d: 50 },
+ { t: "CPU: MOS 6502 @ 1.023 MHz", d: 35 },
+ { t: "Checking devices...", d: 40 },
+ { t: " [OK] Keyboard controller", d: 25 },
+ { t: " [OK] Video adapter (phosphor green)", d: 25 },
+ { t: " [OK] Serial port COM1", d: 25 },
+ { t: " [OK] Floppy drive A:", d: 30 },
+ { t: "", d: 150 },
+ { t: "Loading kernel .........", d: 60 },
+ { t: "INIT: version 1.0 booting", d: 40 },
+ { t: "Mounting filesystems...", d: 45 },
+ { t: " /dev/sda1 on / type ext2 (rw)", d: 30 },
+ { t: " proc on /proc type proc (rw)", d: 25 },
+ { t: "Starting system services:", d: 40 },
+ { t: " * udev [ OK ]", d: 35 },
+ { t: " * networking [ OK ]", d: 35 },
+ { t: " * cron [ OK ]", d: 30 },
+ { t: " * sshd [ OK ]", d: 30 },
+ { t: " * terminal-daemon [ OK ]", d: 35 },
+ { t: "", d: 200 },
+ { t: "System ready.", d: 50 },
+ { t: "Type 'help' for available commands.", d: 40 },
+ { t: "", d: 100 }
+ ];
+
+ function scrollBottom() {
+ screen.scrollTop = screen.scrollHeight;
+ }
+
+ function addLine(text, cls) {
+ var div = document.createElement("div");
+ div.className = "line" + (cls ? " " + cls : "");
+ div.textContent = text;
+ screen.appendChild(div);
+ scrollBottom();
+ return div;
+ }
+
+ function typeText(text, speed, cb) {
+ var div = document.createElement("div");
+ div.className = "line";
+ screen.appendChild(div);
+ var i = 0;
+ function tick() {
+ if (i < text.length) {
+ div.textContent += text.charAt(i);
+ i++;
+ scrollBottom();
+ setTimeout(tick, speed + Math.random() * 20);
+ } else if (cb) {
+ cb();
+ }
+ }
+ tick();
+ }
+
+ function runBoot(index) {
+ if (index >= bootLines.length) {
+ bootDone = true;
+ busy = false;
+ showPrompt();
+ return;
+ }
+ var item = bootLines[index];
+ typeText(item.t, item.d, function () {
+ setTimeout(function () {
+ runBoot(index + 1);
+ }, 40);
+ });
+ }
+
+ function showPrompt() {
+ var wrap = document.createElement("div");
+ wrap.className = "line prompt-line";
+ wrap.id = "activePrompt";
+
+ var prefix = document.createElement("span");
+ prefix.className = "prompt-prefix";
+ prefix.textContent = promptPrefix;
+
+ inputEl = document.createElement("input");
+ inputEl.type = "text";
+ inputEl.id = "cmdInput";
+ inputEl.setAttribute("autocomplete", "off");
+ inputEl.setAttribute("spellcheck", "false");
+ inputEl.setAttribute("autocapitalize", "off");
+
+ var cursor = document.createElement("span");
+ cursor.className = "cursor";
+ cursor.id = "activeCursor";
+
+ wrap.appendChild(prefix);
+ wrap.appendChild(inputEl);
+ wrap.appendChild(cursor);
+ screen.appendChild(wrap);
+ scrollBottom();
+ inputEl.focus();
+
+ inputEl.addEventListener("keydown", onKey);
+ inputEl.addEventListener("input", function () {
+ scrollBottom();
+ });
+ }
+
+ function removePrompt() {
+ var p = document.getElementById("activePrompt");
+ if (p) p.remove();
+ inputEl = null;
+ }
+
+ function freezePrompt(cmd) {
+ var p = document.getElementById("activePrompt");
+ if (!p) return;
+ p.removeAttribute("id");
+ var inp = p.querySelector("input");
+ var cur = p.querySelector(".cursor");
+ if (cur) cur.remove();
+ if (inp) {
+ var span = document.createElement("span");
+ span.textContent = cmd;
+ p.replaceChild(span, inp);
+ }
+ inputEl = null;
+ }
+
+ function onKey(e) {
+ if (!bootDone || busy) {
+ e.preventDefault();
+ return;
+ }
+ if (e.key === "Enter") {
+ e.preventDefault();
+ var cmd = (inputEl.value || "").trim();
+ freezePrompt(cmd);
+ busy = true;
+ handleCommand(cmd, function () {
+ busy = false;
+ showPrompt();
+ });
+ }
+ }
+
+ function handleCommand(raw, done) {
+ var parts = raw.replace(/\s+/g, " ").trim().split(" ");
+ var cmd = (parts[0] || "").toLowerCase();
+ var args = parts.slice(1);
+
+ if (!cmd) {
+ done();
+ return;
+ }
+
+ switch (cmd) {
+ case "help":
+ addLine("");
+ addLine("Available commands:", "bright");
+ addLine(" help Show this help message");
+ addLine(" date Display current date and time");
+ addLine(" clear Clear the terminal screen");
+ addLine(" echo Echo arguments back");
+ addLine(" whoami Print current user");
+ addLine(" uname Print system information");
+ addLine(" ls List directory contents");
+ addLine(" about About this terminal");
+ addLine("");
+ done();
+ break;
+
+ case "date":
+ addLine(new Date().toString());
+ done();
+ break;
+
+ case "clear":
+ screen.innerHTML = "";
+ done();
+ break;
+
+ case "echo":
+ addLine(args.join(" ") || "");
+ done();
+ break;
+
+ case "whoami":
+ addLine("user");
+ done();
+ break;
+
+ case "uname":
+ addLine("CRT-OS 1.0.0 phosphor-green i6502 GNU/Retro");
+ done();
+ break;
+
+ case "ls":
+ addLine("bin boot dev etc home lib proc tmp usr var");
+ done();
+ break;
+
+ case "about":
+ addLine("");
+ addLine("CRT Terminal Emulator v1.0", "bright");
+ addLine("Green phosphor · scanlines · pure nostalgia");
+ addLine("No network. No cloud. Just electrons on glass.");
+ addLine("");
+ done();
+ break;
+
+ default:
+ addLine("sh: " + cmd + ": command not found");
+ done();
+ break;
+ }
+ }
+
+ document.addEventListener("click", function () {
+ if (inputEl) inputEl.focus();
+ });
+
+ setTimeout(function () {
+ runBoot(0);
+ }, 300);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f1756a933e20/grok.png b/data/artifacts/f1756a933e20/grok.png
new file mode 100644
index 0000000..79a4b10
Binary files /dev/null and b/data/artifacts/f1756a933e20/grok.png differ
diff --git a/data/artifacts/f1756a933e20/hermes3-8b.html b/data/artifacts/f1756a933e20/hermes3-8b.html
new file mode 100644
index 0000000..a5207b0
--- /dev/null
+++ b/data/artifacts/f1756a933e20/hermes3-8b.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Retro CRT Terminal Boot Sequence</title>
+ <style>
+ body {
+ background-color: black;
+ color: green;
+ font-family: monospace;
+ font-size: 12px;
+ }
+
+ .scanline {
+ height: 1px;
+ background-color: grey;
+ }
+
+ #terminal {
+ margin-top: 20px;
+ }
+
+ @keyframes flicker {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.5; }
+ }
+
+ .flicker {
+ animation: flicker 1s infinite;
+ }
+ </style>
+</head>
+<body>
+ <div class="scanline"></div>
+ <div class="scanline"></div>
+ <div class="scanline"></div>
+ <div class="flicker">
+ Booting...
+ <div class="scanline"></div>
+ <div class="scanline"></div>
+ <div id="terminal">
+ <pre>
+ Loading core modules...
+ [OK]
+
+ Initializing system services...
+ [OK]
+
+ Starting network interfaces...
+ [OK]
+
+ Mounting filesystems...
+ [OK]
+
+ System boot completed in 4.62 seconds
+ with 1 processes running.
+ </pre>
+ </div>
+ <div class="scanline"></div>
+ <div class="scanline"></div>
+ </div>
+
+ <div class="scanline"></div>
+ <div class="scanline"></div>
+ <div id="prompt" contenteditable></div>
+
+ <script>
+ document.getElementById('prompt').addEventListener('input', function(e) {
+ const input = e.target;
+
+ if (input.textContent === 'help') {
+ input.textContent = '';
+ console.log('Available commands: date, clear, help');
+ } else if (input.textContent === 'date') {
+ input.textContent = '';
+ console.log('Current Date: 2023-04-14');
+ } else if (input.textContent === 'clear') {
+ input.textContent = '';
+ console.clear();
+ }
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f1756a933e20/hermes3-8b.png b/data/artifacts/f1756a933e20/hermes3-8b.png
new file mode 100644
index 0000000..e362628
Binary files /dev/null and b/data/artifacts/f1756a933e20/hermes3-8b.png differ
diff --git a/data/artifacts/f1756a933e20/hf-qwen-coder-32b.html b/data/artifacts/f1756a933e20/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..ca1da30
--- /dev/null
+++ b/data/artifacts/f1756a933e20/hf-qwen-coder-32b.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>RETRO CRT BOOT</title>
+ <style>
+ body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: black; color: lime; font-family: 'Courier New', Courier, monospace; overflow: hidden; }
+ #terminal { width: 80%; max-width: 600px; white-space: pre-wrap; line-height: 1.2em; position: relative; }
+ .scanline { background: repeating-linear-gradient(to bottom, transparent 0%, transparent 49%, rgba(0, 255, 0, 0.03) 50%, rgba(0, 255, 0, 0.03) 51%, transparent 52%); pointer-events: none; position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 100; }
+ .flicker { animation: flicker 1s infinite alternate; }
+ @keyframes flicker {
+ from { opacity: 1; }
+ to { opacity: 0.85; }
+ }
+ #prompt::after { content: '_'; animation: blink 1s step-end infinite; }
+ @keyframes blink {
+ from, to { color: lime; }
+ 50% { color: transparent; }
+ }
+ </style>
+</head>
+<body>
+ <div id="terminal">
+ <div class="flicker">Booting RETRO OS...</div>
+ <div class="flicker">Loading modules...</div>
+ <div class="flicker">Initializing hardware...</div>
+ <div class="flicker">Starting system services...</div>
+ <div class="flicker">System ready.</div>
+ <br><span id="prompt"></span>
+ </div>
+ <div class="scanline"></div>
+ <script>
+ const terminal = document.getElementById('terminal');
+ const promptElement = document.getElementById('prompt');
+ let commandBuffer = '';
+ let commands = {
+ 'help': 'Available commands:\n- help\n- date\n- clear',
+ 'date': new Date().toLocaleString(),
+ 'clear': () => { while (terminal.lastChild !== promptElement) terminal.removeChild(terminal.lastChild); }
+ };
+
+ function addText(text, delay = 50) {
+ return new Promise(resolve => {
+ let i = 0;
+ const interval = setInterval(() => {
+ if (i < text.length) {
+ terminal.innerHTML += text.charAt(i);
+ i++;
+ } else {
+ clearInterval(interval);
+ resolve();
+ }
+ }, delay);
+ });
+ }
+
+ async function bootSequence() {
+ await addText('\n\nBooting RETRO OS...\nLoading modules...\nInitializing hardware...\nStarting system services...\nSystem ready.\n', 150);
+ promptElement.focus = true;
+ document.addEventListener('keydown', handleInput);
+ }
+
+ function handleInput(e) {
+ if (e.key === 'Enter') {
+ executeCommand();
+ } else if (e.key === 'Backspace') {
+ commandBuffer = commandBuffer.slice(0, -1);
+ promptElement.textContent = commandBuffer;
+ } else if (e.key.length === 1 && e.key.match(/[a-z]/i)) {
+ commandBuffer += e.key.toLowerCase();
+ promptElement.textContent = commandBuffer;
+ }
+ }
+
+ function executeCommand() {
+ const response = commands[commandBuffer] || `Unknown command: ${commandBuffer}`;
+ terminal.innerHTML += `\n${commandBuffer}\n`;
+ if (typeof response === 'function') {
+ response();
+ } else {
+ addText(response + '\n');
+ }
+ commandBuffer = '';
+ promptElement.textContent = commandBuffer;
+ }
+
+ bootSequence();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f1756a933e20/hf-qwen-coder-32b.png b/data/artifacts/f1756a933e20/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..498c666
Binary files /dev/null and b/data/artifacts/f1756a933e20/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/f1756a933e20/kimi.html b/data/artifacts/f1756a933e20/kimi.html
new file mode 100644
index 0000000..31196e4
--- /dev/null
+++ b/data/artifacts/f1756a933e20/kimi.html
@@ -0,0 +1,264 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>CRT Terminal</title>
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ body {
+ background: #050505;
+ color: #33ff33;
+ font-family: 'Courier New', Courier, monospace;
+ font-size: 15px;
+ line-height: 1.4;
+ overflow: hidden;
+ height: 100vh;
+ width: 100vw;
+ }
+
+ #terminal {
+ height: 100%;
+ width: 100%;
+ padding: 30px;
+ position: relative;
+ overflow-y: auto;
+ text-shadow: 0 0 2px #33ff33, 0 0 8px #1aff1a;
+ background: radial-gradient(ellipse at center, #0a1a0a 0%, #000000 100%);
+ }
+
+ #terminal::before {
+ content: "";
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(
+ to bottom,
+ rgba(255,255,255,0) 50%,
+ rgba(0,0,0,0.25) 50%
+ );
+ background-size: 100% 4px;
+ pointer-events: none;
+ z-index: 100;
+ }
+
+ @keyframes flicker {
+ 0% { opacity: 0.97; }
+ 5% { opacity: 0.93; }
+ 10% { opacity: 0.95; }
+ 15% { opacity: 0.99; }
+ 20% { opacity: 0.94; }
+ 25% { opacity: 0.98; }
+ 30% { opacity: 0.96; }
+ 35% { opacity: 0.94; }
+ 40% { opacity: 0.98; }
+ 45% { opacity: 0.97; }
+ 50% { opacity: 0.95; }
+ 55% { opacity: 0.93; }
+ 60% { opacity: 0.97; }
+ 65% { opacity: 0.95; }
+ 70% { opacity: 0.94; }
+ 75% { opacity: 0.98; }
+ 80% { opacity: 0.96; }
+ 85% { opacity: 0.94; }
+ 90% { opacity: 0.97; }
+ 95% { opacity: 0.95; }
+ 100% { opacity: 0.98; }
+ }
+
+ #terminal {
+ animation: flicker 0.12s infinite;
+ }
+
+ .line {
+ margin-bottom: 2px;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ }
+
+ .prompt-line {
+ display: flex;
+ align-items: center;
+ margin-top: 4px;
+ }
+
+ .prompt {
+ margin-right: 8px;
+ font-weight: bold;
+ }
+
+ #input {
+ background: transparent;
+ border: none;
+ color: #33ff33;
+ font-family: inherit;
+ font-size: inherit;
+ outline: none;
+ width: 100%;
+ caret-color: #33ff33;
+ text-shadow: 0 0 2px #33ff33;
+ }
+
+ .cursor {
+ display: inline-block;
+ width: 10px;
+ height: 18px;
+ background: #33ff33;
+ box-shadow: 0 0 4px #33ff33;
+ animation: blink 1.2s infinite;
+ margin-left: 2px;
+ }
+
+ @keyframes blink {
+ 0%, 45% { opacity: 1; }
+ 50%, 95% { opacity: 0; }
+ }
+
+ .hidden {
+ display: none;
+ }
+ </style>
+</head>
+<body>
+ <div id="terminal">
+ <div id="boot-text"></div>
+ <div id="input-line" class="prompt-line hidden">
+ <span class="prompt">></span>
+ <input type="text" id="input" spellcheck="false" autocomplete="off" autofocus>
+ <span class="cursor"></span>
+ </div>
+ </div>
+
+ <script>
+ const bootText = document.getElementById('boot-text');
+ const inputLine = document.getElementById('input-line');
+ const input = document.getElementById('input');
+ const terminal = document.getElementById('terminal');
+
+ const bootSequence = [
+ "PHOENIX BIOS 4.0 RELEASE 6.0",
+ "COPYRIGHT 1985-1994 PHOENIX TECHNOLOGIES LTD",
+ "",
+ "CPU: INTEL 486DX2-66",
+ "640K SYSTEM RAM PASSED",
+ "31744K EXTENDED RAM PASSED",
+ "",
+ "AWARD MODULAR BIOS v4.50G",
+ "",
+ "DETECTING IDE PRIMARY MASTER ... WDC AC21200F",
+ "DETECTING IDE PRIMARY SLAVE ... NONE",
+ "DETECTING IDE SECONDARY MASTER ... CD-ROM",
+ "",
+ "LOADING OPERATING SYSTEM...",
+ "[ OK ] MOUNTING ROOT FILESYSTEM",
+ "[ OK ] INITIALIZING NETWORK STACK",
+ "[ OK ] STARTING SYSTEM LOGGING",
+ "[ OK ] MOUNTING EXTERNAL DRIVES",
+ "[ OK ] INITIALIZING TERMINAL INTERFACE",
+ "",
+ "KERNEL 2.4.1-BOOT LOADED",
+ "TYPE 'help' FOR COMMAND LIST",
+ ""
+ ];
+
+ function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+
+ async function typeBootSequence() {
+ for (let i = 0; i < bootSequence.length; i++) {
+ const line = bootSequence[i];
+ const div = document.createElement('div');
+ div.className = 'line';
+ bootText.appendChild(div);
+
+ if (line.startsWith('[') || line.includes('...') || line === '') {
+ div.textContent = line;
+ await sleep(80);
+ } else {
+ for (let j = 0; j < line.length; j++) {
+ div.textContent += line[j];
+ await sleep(8);
+ }
+ await sleep(150);
+ }
+
+ terminal.scrollTop = terminal.scrollHeight;
+ }
+
+ await sleep(400);
+ inputLine.classList.remove('hidden');
+ input.focus();
+ }
+
+ function processCommand(cmd) {
+ const cmdLine = document.createElement('div');
+ cmdLine.className = 'line';
+ cmdLine.textContent = '>' + cmd;
+ bootText.appendChild(cmdLine);
+
+ const args = cmd.trim().toLowerCase().split(/\s+/);
+ const command = args[0];
+
+ const output = document.createElement('div');
+ output.className = 'line';
+
+ switch(command) {
+ case 'help':
+ output.textContent = "AVAILABLE COMMANDS:\n help - Display this help text\n date - Show current system date\n clear - Clear terminal buffer\n ver - Show version info";
+ break;
+ case 'date':
+ const now = new Date();
+ output.textContent = now.toLocaleString();
+ break;
+ case 'clear':
+ bootText.innerHTML = '';
+ terminal.scrollTop = 0;
+ return;
+ case 'ver':
+ output.textContent = "RETRO-OS KERNEL v2.4.1 [BUILD 1986]\nCOPYRIGHT (C) 1986-2024";
+ break;
+ case '':
+ break;
+ default:
+ output.textContent = "UNKNOWN COMMAND: " + command;
+ }
+
+ if (output.textContent) {
+ bootText.appendChild(output);
+ }
+
+ terminal.scrollTop = terminal.scrollHeight;
+ }
+
+ input.addEventListener('keydown', function(e) {
+ if (e.key === 'Enter') {
+ const cmd = this.value;
+ if (cmd.trim()) {
+ processCommand(cmd);
+ } else {
+ const emptyLine = document.createElement('div');
+ emptyLine.className = 'line';
+ emptyLine.textContent = '>';
+ bootText.appendChild(emptyLine);
+ terminal.scrollTop = terminal.scrollHeight;
+ }
+ this.value = '';
+ }
+ });
+
+ terminal.addEventListener('click', () => {
+ input.focus();
+ });
+
+ typeBootSequence();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f1756a933e20/kimi.png b/data/artifacts/f1756a933e20/kimi.png
new file mode 100644
index 0000000..2fb054e
Binary files /dev/null and b/data/artifacts/f1756a933e20/kimi.png differ
diff --git a/data/artifacts/f1756a933e20/qwen25-7b.html b/data/artifacts/f1756a933e20/qwen25-7b.html
new file mode 100644
index 0000000..0bf8cfe
--- /dev/null
+++ b/data/artifacts/f1756a933e20/qwen25-7b.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Retro CRT Terminal</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: black; }
+ .terminal { position: absolute; width: 100%; height: 100%; padding: 5px; font-family: 'Courier New', Courier, monospace; color: limegreen; text-align: left; line-height: 1.3em; background-color: black; }
+ .boot { animation: flicker 2s infinite; }
+ @keyframes flicker { 0% { opacity: 1; } 50% { opacity: 0.7; } 100% { opacity: 1; } }
+ </style>
+</head>
+<body>
+ <div id="terminal" class="terminal"></div>
+ <script>
+ const terminal = document.getElementById('terminal');
+
+ let bootLog = [
+ "Booting retro CRT terminal...",
+ "Loading BIOS... [0%]",
+ " [10%] [20%] [30%] [40%] [50%]",
+ " [60%] [70%] [80%] [90%] [100%]",
+ "",
+ "System initialized",
+ "Loading shell... [0%]",
+ " [10%] [20%] [30%] [40%] [50%]",
+ " [60%] [70%] [80%] [90%] [100%]",
+ "",
+ "Shell ready",
+ "> "
+ ];
+
+ function boot() {
+ for (let i = 0; i < bootLog.length; i++) {
+ setTimeout(() => {
+ terminal.innerHTML += `${bootLog[i]}<br>`;
+ if ((i + 1) === bootLog.length) {
+ setTimeout(() => { terminal.innerHTML += "> "; }, 2000);
+ }
+ }, (i * 500));
+ }
+ }
+
+ function handleInput(event) {
+ const command = event.target.value;
+ switch (command) {
+ case "help":
+ terminal.innerHTML += "<br>Available commands: help, date, clear<br>";
+ break;
+ case "date":
+ terminal.innerHTML += `<br>Today's date: ${new Date().toLocaleDateString()}<br>`;
+ break;
+ case "clear":
+ terminal.innerHTML = "> ";
+ break;
+ }
+ }
+
+ document.addEventListener("DOMContentLoaded", () => {
+ boot();
+ setTimeout(() => { document.getElementById('terminal').focus(); }, 1000);
+ });
+
+ document.getElementById('terminal').addEventListener('keydown', handleInput);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f1756a933e20/qwen25-7b.png b/data/artifacts/f1756a933e20/qwen25-7b.png
new file mode 100644
index 0000000..7da864f
Binary files /dev/null and b/data/artifacts/f1756a933e20/qwen25-7b.png differ
diff --git a/data/artifacts/f1756a933e20/qwen3-14b.html b/data/artifacts/f1756a933e20/qwen3-14b.html
new file mode 100644
index 0000000..2cdac29
--- /dev/null
+++ b/data/artifacts/f1756a933e20/qwen3-14b.html
@@ -0,0 +1,156 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>Retro CRT Terminal</title>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ background: #000;
+ overflow: hidden;
+ font-family: 'Courier New', Courier, monospace;
+ color: #0f0;
+ user-select: none;
+ }
+ #terminal {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ padding: 20px;
+ box-sizing: border-box;
+ overflow: hidden;
+ background: #000;
+ text-shadow: 0 0 5px #000, 0 0 10px #0f0;
+ }
+ #screen {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background: #000;
+ overflow: auto;
+ padding: 0;
+ margin: 0;
+ line-height: 1.2;
+ font-size: 14px;
+ white-space: pre;
+ word-wrap: normal;
+ }
+ #cursor {
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100%;
+ width: 2px;
+ background: #0f0;
+ animation: blink 1s infinite;
+ }
+ @keyframes blink {
+ 50% { opacity: 0; }
+ }
+ body::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: radial-gradient(circle at 50% 50%, rgba(0, 255, 0, 0.05) 0%, rgba(0, 0, 0, 0.5) 100%);
+ pointer-events: none;
+ z-index: 2;
+ }
+ </style>
+</head>
+<body>
+ <div id="terminal">
+ <div id="screen" contenteditable="false"></div>
+ <div id="cursor"></div>
+ </div>
+ <script>
+ const screen = document.getElementById("screen");
+ const cursor = document.getElementById("cursor");
+ let typing = true;
+ let line = 0;
+ let typingIndex = 0;
+ let inputLine = "";
+ let inputPos = 0;
+ let typingText = [
+ "Initializing core systems...",
+ "Loading kernel modules...",
+ "Detecting hardware...",
+ "Initializing graphics subsystem...",
+ "Initializing network stack...",
+ "Memory check: OK",
+ "Disk check: OK",
+ "Security check: OK",
+ "System ready. Welcome to RetroOS v1.0",
+ "root@retroos:~# "
+ ];
+
+ function typeText(text) {
+ if (typingIndex >= text.length) {
+ typingIndex = 0;
+ line++;
+ if (line < typingText.length) {
+ setTimeout(() => typeText(typingText[line]), 100);
+ } else {
+ typing = false;
+ cursor.style.left = "0px";
+ screen.innerHTML += "\nroot@retroos:~# ";
+ document.addEventListener("keydown", handleKey);
+ }
+ } else {
+ screen.innerHTML += text[typingIndex];
+ typingIndex++;
+ setTimeout(() => typeText(text), 30);
+ }
+ }
+
+ function handleKey(e) {
+ if (typing) return;
+ if (e.key === "Backspace") {
+ if (inputPos > 0) {
+ inputPos--;
+ inputLine = inputLine.slice(0, -1);
+ screen.innerHTML = screen.innerHTML.slice(0, -1);
+ cursor.style.left = `${inputPos * 8}px`;
+ }
+ } else if (e.key === "Enter") {
+ screen.innerHTML += "\n";
+ processCommand(inputLine);
+ inputLine = "";
+ inputPos = 0;
+ screen.innerHTML += "root@retroos:~# ";
+ cursor.style.left = "0px";
+ } else if (e.key.length === 1) {
+ inputLine += e.key;
+ screen.innerHTML += e.key;
+ inputPos++;
+ cursor.style.left = `${inputPos * 8}px`;
+ }
+ }
+
+ function processCommand(cmd) {
+ screen.innerHTML += "\n";
+ if (cmd === "help") {
+ screen.innerHTML += "Available commands: help, date, clear\n";
+ } else if (cmd === "date") {
+ screen.innerHTML += `Current date: ${new Date().toLocaleString()}\n`;
+ } else if (cmd === "clear") {
+ screen.innerHTML = "";
+ typing = true;
+ line = 0;
+ typingIndex = 0;
+ typeText(typingText[line]);
+ } else {
+ screen.innerHTML += `Command not found: ${cmd}\n`;
+ }
+ }
+
+ typeText(typingText[line]);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/f1756a933e20/qwen3-14b.png b/data/artifacts/f1756a933e20/qwen3-14b.png
new file mode 100644
index 0000000..cbf88db
Binary files /dev/null and b/data/artifacts/f1756a933e20/qwen3-14b.png differ
diff --git a/data/challenges.json b/data/challenges.json
index 7908f6b..0876765 100644
--- a/data/challenges.json
+++ b/data/challenges.json
@@ -3722,6 +3722,1087 @@
"finished_at": "2026-07-23T16:16:37.703Z",
"queued_at": "2026-07-23T16:11:07.255Z",
"bytes": 5028,
+ "thumb": true,
+ "aiScore": 6.3,
+ "aiReason": "The dashboard includes basic elements but lacks the animated updates and sorting functionality required by the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 6,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T16:22:23.493Z",
+ "aiPick": "gemma3-12b"
+ },
+ {
+ "id": "78fea53364f0",
+ "title": "Conway Life Painter",
+ "prompt": "Build a single-file HTML page: an interactive Conway's Game of Life. Click/drag to paint live cells on a grid, play/pause, step, speed slider, randomize, clear, and a few preset patterns (glider, pulsar). Wrap-around edges, generation counter. Canvas-based, self-contained, no external libraries.",
+ "category": "Games",
+ "designTools": false,
+ "created_at": "2026-07-23T16:17:05.391Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 157,
+ "cost": 0,
+ "started_at": "2026-07-23T16:17:11.804Z",
+ "finished_at": "2026-07-23T16:19:48.454Z",
+ "queued_at": "2026-07-23T16:17:05.395Z",
+ "bytes": 7663,
+ "thumb": true,
+ "aiScore": 8,
+ "aiReason": "The game is fully functional and visually clear, meeting all specified requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7
+ },
+ "aiSpread": 2
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 121,
+ "cost": 0,
+ "started_at": "2026-07-23T16:19:48.455Z",
+ "finished_at": "2026-07-23T16:21:49.635Z",
+ "queued_at": "2026-07-23T16:17:05.398Z",
+ "bytes": 7025,
+ "thumb": true,
+ "aiScore": 7.8,
+ "aiReason": "The page is fully functional and meets all the criteria of the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 21,
+ "cost": 0,
+ "started_at": "2026-07-23T16:21:49.636Z",
+ "finished_at": "2026-07-23T16:22:10.354Z",
+ "queued_at": "2026-07-23T16:17:05.401Z",
+ "bytes": 6215,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is functional but lacks some preset patterns and the generation counter.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "aiPick": "qwen3-14b",
+ "judged_at": "2026-07-23T16:27:34.571Z"
+ },
+ {
+ "id": "c21d462d6163",
+ "title": "Penrose Tiling Generator",
+ "prompt": "Build a single-file HTML page that draws an animated Penrose P3 rhombus tiling on a canvas via deflation (subdividing fat/thin rhombi). Controls: subdivision-depth slider, color-by-rhombus-type toggle, and a randomize-palette button. Self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T16:25:23.304Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 34,
+ "cost": 0,
+ "started_at": "2026-07-23T16:25:24.423Z",
+ "finished_at": "2026-07-23T16:25:58.584Z",
+ "queued_at": "2026-07-23T16:25:23.306Z",
+ "bytes": 4806,
+ "thumb": true,
+ "aiScore": 6.3,
+ "aiReason": "The page is empty and does not display any content or functionality related to the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 5,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 45,
+ "cost": 0,
+ "started_at": "2026-07-23T16:25:58.586Z",
+ "finished_at": "2026-07-23T16:26:43.204Z",
+ "queued_at": "2026-07-23T16:25:23.309Z",
+ "bytes": 5165,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The page successfully implements the required controls and displays an animated Penrose P3 tiling. However, it lacks a randomize palette button.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 25,
+ "cost": 0,
+ "started_at": "2026-07-23T16:26:43.205Z",
+ "finished_at": "2026-07-23T16:27:07.776Z",
+ "queued_at": "2026-07-23T16:25:23.312Z",
+ "bytes": 3785,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The page is functional but lacks visual appeal and refinement.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "aiPick": "gemma3-12b",
+ "judged_at": "2026-07-23T16:30:49.146Z"
+ },
+ {
+ "id": "2e0c0d48120e",
+ "title": "Starfield Warp",
+ "prompt": "Build a single-file HTML page: a 3D starfield warp effect. Stars stream toward the viewer with speed controlled by mouse Y; click to hyperjump (burst of speed + motion blur). Canvas-based, self-contained, no external libraries.",
+ "category": "Games",
+ "designTools": false,
+ "created_at": "2026-07-23T16:27:55.015Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 18,
+ "cost": 0,
+ "started_at": "2026-07-23T16:27:56.341Z",
+ "finished_at": "2026-07-23T16:28:14.415Z",
+ "queued_at": "2026-07-23T16:27:55.017Z",
+ "bytes": 2127,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The effect is visually engaging but lacks the hyperjump feature and motion blur.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 24,
+ "cost": 0,
+ "started_at": "2026-07-23T16:28:14.418Z",
+ "finished_at": "2026-07-23T16:28:38.786Z",
+ "queued_at": "2026-07-23T16:27:55.022Z",
+ "bytes": 3232,
+ "thumb": true,
+ "aiScore": 7,
+ "aiReason": "The page demonstrates the starfield effect but lacks interactivity and visual polish.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 11,
+ "cost": 0,
+ "started_at": "2026-07-23T16:28:38.788Z",
+ "finished_at": "2026-07-23T16:28:49.475Z",
+ "queued_at": "2026-07-23T16:27:55.025Z",
+ "bytes": 2327,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The effect is visually appealing but lacks the hyperjump feature and mouse control.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T16:31:00.195Z",
+ "aiPick": "hermes3-8b"
+ },
+ {
+ "id": "789f61c347ae",
+ "title": "Bouncing Ball Symphony",
+ "prompt": "Build a single-file HTML page: colorful balls drop and bounce with gravity and elastic collisions inside a spinning hexagon container; each wall-hit plays a soft synth tone (WebAudio). Add-ball button, gravity slider. Canvas-based, self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T16:29:02.937Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 39,
+ "cost": 0,
+ "started_at": "2026-07-23T16:29:09.157Z",
+ "finished_at": "2026-07-23T16:29:47.917Z",
+ "queued_at": "2026-07-23T16:29:02.940Z",
+ "bytes": 6074,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The model successfully implements the challenge requirements but lacks visual appeal and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 34,
+ "cost": 0,
+ "started_at": "2026-07-23T16:29:47.921Z",
+ "finished_at": "2026-07-23T16:30:21.480Z",
+ "queued_at": "2026-07-23T16:29:02.943Z",
+ "bytes": 3931,
+ "thumb": true,
+ "aiScore": 4,
+ "aiReason": "The image is blank and does not show any balls or a spinning hexagon container.",
+ "aiScores": {
+ "qwen2.5vl:7b": 4,
+ "minicpm-v:latest": 4
+ },
+ "aiSpread": 0
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 16,
+ "cost": 0,
+ "started_at": "2026-07-23T16:30:21.483Z",
+ "finished_at": "2026-07-23T16:30:37.460Z",
+ "queued_at": "2026-07-23T16:29:02.946Z",
+ "bytes": 5713,
+ "thumb": true,
+ "aiScore": 5.3,
+ "aiReason": "The model fulfills the challenge but lacks visual appeal and sound effects.",
+ "aiScores": {
+ "qwen2.5vl:7b": 6,
+ "minicpm-v:latest": 4.5
+ },
+ "aiSpread": 1.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T16:32:30.756Z",
+ "aiPick": "qwen3-14b"
+ },
+ {
+ "id": "a353bd10ea1b",
+ "title": "Mandelbrot Explorer",
+ "prompt": "Build a single-file HTML page: an interactive Mandelbrot set explorer. Click to zoom in (scroll or buttons to zoom out), smooth color-cycling palette, iteration slider, and a reset. Render on canvas with escape-time coloring. Self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T16:30:58.890Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 34,
+ "cost": 0,
+ "started_at": "2026-07-23T16:31:00.196Z",
+ "finished_at": "2026-07-23T16:31:34.351Z",
+ "queued_at": "2026-07-23T16:30:58.892Z",
+ "bytes": 4674,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is interactive and fulfills the requirements but lacks visual appeal due to a plain background.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 29,
+ "cost": 0,
+ "started_at": "2026-07-23T16:31:34.355Z",
+ "finished_at": "2026-07-23T16:32:03.706Z",
+ "queued_at": "2026-07-23T16:30:58.894Z",
+ "bytes": 3423,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is interactive and fulfills the requirements but lacks visual quality due to a blank canvas.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 11,
+ "cost": 0,
+ "started_at": "2026-07-23T16:32:03.708Z",
+ "finished_at": "2026-07-23T16:32:14.817Z",
+ "queued_at": "2026-07-23T16:30:58.896Z",
+ "bytes": 3055,
+ "thumb": true,
+ "aiScore": 5.5,
+ "aiReason": "Basic functionality but lacks smooth color cycling and iteration slider.",
+ "aiScores": {
+ "qwen2.5vl:7b": 5,
+ "minicpm-v:latest": 6
+ },
+ "aiSpread": 1
+ }
+ ],
+ "judging": false,
+ "aiPick": "qwen3-14b",
+ "judged_at": "2026-07-23T16:35:24.318Z"
+ },
+ {
+ "id": "6acc7c672157",
+ "title": "All-Models Showdown — Particle Constellation",
+ "prompt": "Build a single-file HTML page: an interactive particle constellation. Points drift across a dark canvas; lines connect nearby points; the mouse attracts/repels them and spawns bursts on click. Smooth, performant, self-contained, no external libraries.",
+ "category": "Games",
+ "designTools": false,
+ "created_at": "2026-07-23T16:32:53.392Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 28,
+ "cost": 0,
+ "started_at": "2026-07-23T16:32:53.423Z",
+ "finished_at": "2026-07-23T16:33:21.118Z",
+ "queued_at": "2026-07-23T16:32:53.396Z",
+ "bytes": 3422,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The particle constellation is interactive and visually appealing, but lacks some polish in terms of smoothness and performance.",
+ "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-23T16:33:21.122Z",
+ "finished_at": "2026-07-23T16:33:54.100Z",
+ "queued_at": "2026-07-23T16:32:53.399Z",
+ "bytes": 4880,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The model fulfills the challenge requirements but lacks some visual polish and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 17,
+ "cost": 0,
+ "started_at": "2026-07-23T16:33:54.102Z",
+ "finished_at": "2026-07-23T16:34:10.619Z",
+ "queued_at": "2026-07-23T16:32:53.403Z",
+ "bytes": 5458,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The page is interactive and fulfills the challenge requirements, but lacks visual appeal.",
+ "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-23T16:32:53.425Z",
+ "finished_at": "2026-07-23T16:33:17.149Z",
+ "queued_at": "2026-07-23T16:32:53.407Z",
+ "bytes": 3221,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is interactive and fulfills the challenge requirements, but lacks visual appeal.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 41,
+ "cost": 0,
+ "started_at": "2026-07-23T16:34:10.622Z",
+ "finished_at": "2026-07-23T16:34:52.103Z",
+ "queued_at": "2026-07-23T16:32:53.410Z",
+ "bytes": 2951,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The particle constellation is interactive and visually appealing, but the performance could be smoother.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 52,
+ "cost": 0,
+ "started_at": "2026-07-23T16:32:53.427Z",
+ "finished_at": "2026-07-23T16:33:45.054Z",
+ "queued_at": "2026-07-23T16:32:53.413Z",
+ "bytes": 9333,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The model fulfills the challenge requirements and has smooth performance with a visually appealing particle constellation.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 95,
+ "cost": 0.0114,
+ "started_at": "2026-07-23T16:32:53.442Z",
+ "finished_at": "2026-07-23T16:34:28.256Z",
+ "queued_at": "2026-07-23T16:32:53.416Z",
+ "bytes": 8755,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The model successfully implements an interactive particle constellation with mouse attraction/repulsion and click-to-spawn functionality, adhering to the challenge's requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 42,
+ "cost": 0.0403,
+ "started_at": "2026-07-23T16:32:53.452Z",
+ "finished_at": "2026-07-23T16:33:35.795Z",
+ "queued_at": "2026-07-23T16:32:53.418Z",
+ "bytes": 10686,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The particle constellation is interactive and visually appealing, but the visual quality could be improved by adding more particles or adjusting their density for a fuller effect.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 31,
+ "cost": 0.0365,
+ "started_at": "2026-07-23T16:32:53.453Z",
+ "finished_at": "2026-07-23T16:33:24.743Z",
+ "queued_at": "2026-07-23T16:32:53.420Z",
+ "bytes": 6031,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The particle constellation is visually appealing and fulfills the challenge requirements smoothly.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T16:39:15.187Z",
+ "aiPick": "claude-code"
+ },
+ {
+ "id": "edbe5de71e26",
+ "title": "All-Models — Fluid Ink Sim",
+ "prompt": "Build a single-file HTML page: an interactive fluid/ink simulation. Dragging the mouse injects swirling colored ink that diffuses and advects across a dark canvas; a clear button and a color picker. Grid-based fluid solver, self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T16:35:42.481Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 43,
+ "cost": 0,
+ "started_at": "2026-07-23T16:35:43.765Z",
+ "finished_at": "2026-07-23T16:36:26.982Z",
+ "queued_at": "2026-07-23T16:35:42.483Z",
+ "bytes": 6106,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is interactive and fulfills the challenge requirements but lacks visual appeal due to a monochromatic color scheme.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 42,
+ "cost": 0,
+ "started_at": "2026-07-23T16:36:26.985Z",
+ "finished_at": "2026-07-23T16:37:08.870Z",
+ "queued_at": "2026-07-23T16:35:42.486Z",
+ "bytes": 5647,
+ "thumb": true,
+ "aiScore": 6.3,
+ "aiReason": "The simulation is functional but lacks visual appeal and detail.",
+ "aiScores": {
+ "qwen2.5vl:7b": 6,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 12,
+ "cost": 0,
+ "started_at": "2026-07-23T16:37:08.872Z",
+ "finished_at": "2026-07-23T16:37:21.245Z",
+ "queued_at": "2026-07-23T16:35:42.488Z",
+ "bytes": 3180,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is functional but lacks visual appeal and detail.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 24,
+ "cost": 0,
+ "started_at": "2026-07-23T16:35:42.506Z",
+ "finished_at": "2026-07-23T16:36:06.198Z",
+ "queued_at": "2026-07-23T16:35:42.492Z",
+ "bytes": 3907,
+ "thumb": true,
+ "aiScore": 5.8,
+ "aiReason": "The page is interactive and fulfills the challenge requirements but lacks visual appeal due to a monochromatic color scheme.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 4.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 114,
+ "cost": 0,
+ "started_at": "2026-07-23T16:37:21.247Z",
+ "finished_at": "2026-07-23T16:39:15.183Z",
+ "queued_at": "2026-07-23T16:35:42.494Z",
+ "bytes": 6395,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page is interactive and fulfills the challenge requirements, but lacks visual appeal due to a lack of color variety.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 104,
+ "cost": 0,
+ "started_at": "2026-07-23T16:35:42.508Z",
+ "finished_at": "2026-07-23T16:37:26.015Z",
+ "queued_at": "2026-07-23T16:35:42.497Z",
+ "bytes": 8613,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The simulation is functional and visually appealing despite the lack of a grid.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 245,
+ "cost": 0.0291,
+ "started_at": "2026-07-23T16:35:42.513Z",
+ "finished_at": "2026-07-23T16:39:47.106Z",
+ "queued_at": "2026-07-23T16:35:42.499Z",
+ "bytes": 12454,
+ "thumb": true,
+ "aiScore": 6.3,
+ "aiReason": "The page has basic functionality but lacks visual appeal and the grid-based solver is not evident.",
+ "aiScores": {
+ "qwen2.5vl:7b": 6,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 34,
+ "cost": 0.0537,
+ "started_at": "2026-07-23T16:35:42.516Z",
+ "finished_at": "2026-07-23T16:36:16.503Z",
+ "queued_at": "2026-07-23T16:35:42.502Z",
+ "bytes": 11693,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page has the basic elements but lacks visual appeal and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 66,
+ "cost": 0.0871,
+ "started_at": "2026-07-23T16:35:42.519Z",
+ "finished_at": "2026-07-23T16:36:48.826Z",
+ "queued_at": "2026-07-23T16:35:42.504Z",
+ "bytes": 15120,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page meets the challenge requirements but lacks visual appeal and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T16:43:13.537Z",
+ "aiPick": "qwen3-14b"
+ },
+ {
+ "id": "f1756a933e20",
+ "title": "All-Models — Retro CRT Terminal",
+ "prompt": "Build a single-file HTML page: a retro CRT terminal boot sequence. Green phosphor text types out a fake boot log with scanlines, flicker, and cursor; then a working prompt that responds to a few typed commands (help, date, clear). Self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T16:40:16.308Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 29,
+ "cost": 0,
+ "started_at": "2026-07-23T16:40:19.662Z",
+ "finished_at": "2026-07-23T16:40:48.284Z",
+ "queued_at": "2026-07-23T16:40:16.311Z",
+ "bytes": 3998,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The page successfully displays the boot log and prompt as required but lacks scanlines and flicker effects.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 39,
+ "cost": 0,
+ "started_at": "2026-07-23T16:40:48.285Z",
+ "finished_at": "2026-07-23T16:41:27.342Z",
+ "queued_at": "2026-07-23T16:40:16.314Z",
+ "bytes": 4027,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The boot sequence is visually engaging and the commands work as expected, but lacks scanlines and flicker.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 13,
+ "cost": 0,
+ "started_at": "2026-07-23T16:41:27.345Z",
+ "finished_at": "2026-07-23T16:41:40.408Z",
+ "queued_at": "2026-07-23T16:40:16.316Z",
+ "bytes": 1609,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The page successfully simulates a CRT terminal boot sequence but lacks the scanlines and flicker effects.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 15,
+ "cost": 0,
+ "started_at": "2026-07-23T16:40:16.337Z",
+ "finished_at": "2026-07-23T16:40:31.681Z",
+ "queued_at": "2026-07-23T16:40:16.319Z",
+ "bytes": 2399,
+ "thumb": true,
+ "aiScore": 7.8,
+ "aiReason": "The HTML page effectively simulates a CRT terminal boot sequence with green text and scanlines, fulfilling the challenge requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 44,
+ "cost": 0,
+ "started_at": "2026-07-23T16:41:40.412Z",
+ "finished_at": "2026-07-23T16:42:24.463Z",
+ "queued_at": "2026-07-23T16:40:16.324Z",
+ "bytes": 3774,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The HTML page effectively simulates a CRT terminal boot sequence with green text and scanlines, fulfilling the challenge requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 61,
+ "cost": 0,
+ "started_at": "2026-07-23T16:40:16.340Z",
+ "finished_at": "2026-07-23T16:41:17.563Z",
+ "queued_at": "2026-07-23T16:40:16.327Z",
+ "bytes": 9956,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The HTML page successfully mimics a CRT terminal boot sequence with green text and scanlines, fulfilling the challenge requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 94,
+ "cost": 0.0115,
+ "started_at": "2026-07-23T16:40:16.344Z",
+ "finished_at": "2026-07-23T16:41:50.369Z",
+ "queued_at": "2026-07-23T16:40:16.330Z",
+ "bytes": 8349,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The HTML page effectively simulates a CRT terminal boot sequence with green text and scanlines. The commands respond as expected.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 30,
+ "cost": 0.0427,
+ "started_at": "2026-07-23T16:40:16.347Z",
+ "finished_at": "2026-07-23T16:40:46.051Z",
+ "queued_at": "2026-07-23T16:40:16.332Z",
+ "bytes": 9396,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The HTML page effectively simulates a CRT terminal boot sequence with green phosphor text and scanlines, fulfilling the challenge requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 38,
+ "cost": 0.0472,
+ "started_at": "2026-07-23T16:40:16.349Z",
+ "finished_at": "2026-07-23T16:40:54.629Z",
+ "queued_at": "2026-07-23T16:40:16.334Z",
+ "bytes": 9565,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The HTML page effectively simulates a CRT terminal boot sequence with green text and scanlines, fulfilling the challenge requirements.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
+ }
+ ],
+ "judging": false,
+ "judged_at": "2026-07-23T16:43:32.989Z",
+ "aiPick": "claude-code"
+ },
+ {
+ "id": "2ca024f41f3c",
+ "title": "All-Models — Solar System Orrery",
+ "prompt": "Build a single-file HTML page: an interactive solar-system orrery. Planets orbit the sun at proportional speeds with visible orbit rings; hover a planet for its name; a speed slider and a pause button. Canvas-based, self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T16:43:51.781Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 30,
+ "cost": 0,
+ "started_at": "2026-07-23T16:43:51.803Z",
+ "finished_at": "2026-07-23T16:44:21.340Z",
+ "queued_at": "2026-07-23T16:43:51.783Z",
+ "bytes": 4272,
+ "thumb": true,
+ "aiScore": 8,
+ "aiReason": "The model fulfills the challenge requirements but lacks some visual polish and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9
+ },
+ "aiSpread": 2
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 27,
+ "cost": 0,
+ "started_at": "2026-07-23T16:44:21.342Z",
+ "finished_at": "2026-07-23T16:44:48.283Z",
+ "queued_at": "2026-07-23T16:43:51.784Z",
+ "bytes": 3122,
+ "thumb": true,
+ "aiScore": 5.3,
+ "aiReason": "Basic functionality but lacks visual appeal and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 5,
+ "minicpm-v:latest": 5.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 16,
+ "cost": 0,
+ "started_at": "2026-07-23T16:44:48.284Z",
+ "finished_at": "2026-07-23T16:45:04.343Z",
+ "queued_at": "2026-07-23T16:43:51.787Z",
+ "bytes": 5148,
+ "thumb": true,
+ "aiScore": 6,
+ "aiReason": "The model fulfills the challenge requirements but lacks visual appeal and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 5
+ },
+ "aiSpread": 2
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 23,
+ "cost": 0,
+ "started_at": "2026-07-23T16:43:51.805Z",
+ "finished_at": "2026-07-23T16:44:15.026Z",
+ "queued_at": "2026-07-23T16:43:51.789Z",
+ "bytes": 3290,
+ "thumb": true,
+ "aiScore": 6.5,
+ "aiReason": "The model fulfills the challenge requirements but lacks visual appeal and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6
+ },
+ "aiSpread": 1
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 47,
+ "cost": 0,
+ "started_at": "2026-07-23T16:45:04.345Z",
+ "finished_at": "2026-07-23T16:45:51.777Z",
+ "queued_at": "2026-07-23T16:43:51.791Z",
+ "bytes": 3699,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The model fulfills the challenge requirements but lacks some visual polish and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 53,
+ "cost": 0,
+ "started_at": "2026-07-23T16:43:51.808Z",
+ "finished_at": "2026-07-23T16:44:44.461Z",
+ "queued_at": "2026-07-23T16:43:51.794Z",
+ "bytes": 9198,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The model fulfills the challenge requirements but lacks some visual polish and detail.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 165,
+ "cost": 0.0203,
+ "started_at": "2026-07-23T16:43:51.814Z",
+ "finished_at": "2026-07-23T16:46:37.149Z",
+ "queued_at": "2026-07-23T16:43:51.797Z",
+ "bytes": 6680,
+ "thumb": true
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 35,
+ "cost": 0.0388,
+ "started_at": "2026-07-23T16:43:51.818Z",
+ "finished_at": "2026-07-23T16:44:26.637Z",
+ "queued_at": "2026-07-23T16:43:51.798Z",
+ "bytes": 9131,
+ "thumb": true
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 42,
+ "cost": 0.0543,
+ "started_at": "2026-07-23T16:43:51.821Z",
+ "finished_at": "2026-07-23T16:44:33.441Z",
+ "queued_at": "2026-07-23T16:43:51.800Z",
+ "bytes": 9583,
"thumb": true
}
],
@@ -3729,24 +4810,166 @@
"judged_at": null
},
{
- "id": "78fea53364f0",
- "title": "Conway Life Painter",
- "prompt": "Build a single-file HTML page: an interactive Conway's Game of Life. Click/drag to paint live cells on a grid, play/pause, step, speed slider, randomize, clear, and a few preset patterns (glider, pulsar). Wrap-around edges, generation counter. Canvas-based, self-contained, no external libraries.",
+ "id": "c2d8bc75e725",
+ "title": "All-Models — Typing Speed Test",
+ "prompt": "Build a single-file HTML page: a typing speed test. Show a paragraph of sample text; as the user types, correct chars highlight green and errors red; live WPM and accuracy; a restart button with a fresh paragraph. Clean, self-contained, no external libraries.",
+ "category": "Custom",
+ "designTools": false,
+ "created_at": "2026-07-23T16:47:05.089Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 30,
+ "cost": 0,
+ "started_at": "2026-07-23T16:47:05.609Z",
+ "finished_at": "2026-07-23T16:47:35.387Z",
+ "queued_at": "2026-07-23T16:47:05.094Z",
+ "bytes": 4370,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The page is functional but lacks visual appeal and does not fully utilize the available space.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 34,
+ "cost": 0,
+ "started_at": "2026-07-23T16:47:35.391Z",
+ "finished_at": "2026-07-23T16:48:09.631Z",
+ "queued_at": "2026-07-23T16:47:05.097Z",
+ "bytes": 5752,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The page is functional but lacks visual appeal and interactivity.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 16,
+ "cost": 0,
+ "started_at": "2026-07-23T16:48:09.636Z",
+ "finished_at": "2026-07-23T16:48:25.982Z",
+ "queued_at": "2026-07-23T16:47:05.100Z",
+ "bytes": 5309,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The page is functional but lacks visual appeal and clarity in terms of highlighting errors or displaying live WPM/accuracy.",
+ "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-23T16:47:05.117Z",
+ "finished_at": "2026-07-23T16:47:18.623Z",
+ "queued_at": "2026-07-23T16:47:05.102Z",
+ "bytes": 2401,
+ "thumb": true
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "done",
+ "error": null,
+ "seconds": 44,
+ "cost": 0,
+ "started_at": "2026-07-23T16:48:25.986Z",
+ "finished_at": "2026-07-23T16:49:09.648Z",
+ "queued_at": "2026-07-23T16:47:05.105Z",
+ "bytes": 3154,
+ "thumb": true
+ },
+ {
+ "model": "claude-code",
+ "status": "done",
+ "error": null,
+ "seconds": 52,
+ "cost": 0,
+ "started_at": "2026-07-23T16:47:05.119Z",
+ "finished_at": "2026-07-23T16:47:57.347Z",
+ "queued_at": "2026-07-23T16:47:05.108Z",
+ "bytes": 10946,
+ "thumb": true
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 108,
+ "cost": 0.0135,
+ "started_at": "2026-07-23T16:47:05.136Z",
+ "finished_at": "2026-07-23T16:48:52.866Z",
+ "queued_at": "2026-07-23T16:47:05.111Z",
+ "bytes": 7071,
+ "thumb": true
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 25,
+ "cost": 0.0435,
+ "started_at": "2026-07-23T16:47:05.138Z",
+ "finished_at": "2026-07-23T16:47:30.552Z",
+ "queued_at": "2026-07-23T16:47:05.113Z",
+ "bytes": 11395,
+ "thumb": true
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 39,
+ "cost": 0.0448,
+ "started_at": "2026-07-23T16:47:05.139Z",
+ "finished_at": "2026-07-23T16:47:44.290Z",
+ "queued_at": "2026-07-23T16:47:05.115Z",
+ "bytes": 9916,
+ "thumb": true
+ }
+ ],
+ "judging": true,
+ "judged_at": null
+ },
+ {
+ "id": "1fd68f0d37b7",
+ "title": "All-Models — Audio Visualizer",
+ "prompt": "Build a single-file HTML page: a microphone audio visualizer. Ask for mic permission, then render a live frequency-bar + waveform visualization on canvas using the WebAudio AnalyserNode; a color-theme toggle. Graceful message if mic is denied. Self-contained, no external libraries.",
"category": "Games",
"designTools": false,
- "created_at": "2026-07-23T16:17:05.391Z",
+ "created_at": "2026-07-23T16:49:39.482Z",
"winner": null,
"runs": [
{
"model": "qwen3-14b",
"status": "done",
"error": null,
- "seconds": 157,
+ "seconds": 22,
"cost": 0,
- "started_at": "2026-07-23T16:17:11.804Z",
- "finished_at": "2026-07-23T16:19:48.454Z",
- "queued_at": "2026-07-23T16:17:05.395Z",
- "bytes": 7663,
+ "started_at": "2026-07-23T16:49:44.806Z",
+ "finished_at": "2026-07-23T16:50:06.809Z",
+ "queued_at": "2026-07-23T16:49:39.485Z",
+ "bytes": 3032,
"thumb": true
},
{
@@ -3755,9 +4978,9 @@
"error": null,
"seconds": null,
"cost": null,
- "started_at": "2026-07-23T16:19:48.455Z",
+ "started_at": "2026-07-23T16:50:06.811Z",
"finished_at": null,
- "queued_at": "2026-07-23T16:17:05.398Z"
+ "queued_at": "2026-07-23T16:49:39.486Z"
},
{
"model": "hermes3-8b",
@@ -3767,7 +4990,69 @@
"cost": null,
"started_at": null,
"finished_at": null,
- "queued_at": "2026-07-23T16:17:05.401Z"
+ "queued_at": "2026-07-23T16:49:39.489Z"
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 18,
+ "cost": 0,
+ "started_at": "2026-07-23T16:49:39.508Z",
+ "finished_at": "2026-07-23T16:49:57.321Z",
+ "queued_at": "2026-07-23T16:49:39.492Z",
+ "bytes": 3106,
+ "thumb": true
+ },
+ {
+ "model": "hf-qwen-coder-32b",
+ "status": "queued",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": null,
+ "finished_at": null,
+ "queued_at": "2026-07-23T16:49:39.495Z"
+ },
+ {
+ "model": "claude-code",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T16:49:39.511Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T16:49:39.499Z"
+ },
+ {
+ "model": "kimi",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T16:49:39.514Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T16:49:39.502Z"
+ },
+ {
+ "model": "gpt",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T16:49:39.516Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T16:49:39.504Z"
+ },
+ {
+ "model": "grok",
+ "status": "running",
+ "error": null,
+ "seconds": null,
+ "cost": null,
+ "started_at": "2026-07-23T16:49:39.517Z",
+ "finished_at": null,
+ "queued_at": "2026-07-23T16:49:39.506Z"
}
]
}
diff --git a/data/costlog.jsonl b/data/costlog.jsonl
index ad45f27..09bafc6 100644
--- a/data/costlog.jsonl
+++ b/data/costlog.jsonl
@@ -64,3 +64,18 @@
{"ts":"2026-07-23T16:05:53.356Z","provider":"adobe","model":"gemini-image","task":"model-arena-tools","cost_usd":0.04}
{"ts":"2026-07-23T16:06:31.614Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena-tools","input_tokens":2668,"output_tokens":2171,"cost_usd":0.007028}
{"ts":"2026-07-23T16:07:11.408Z","provider":"adobe","model":"gemini-image","task":"model-arena-tools","cost_usd":0.04}
+{"ts":"2026-07-23T16:33:24.737Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":337,"output_tokens":2364,"cost_usd":0.036471}
+{"ts":"2026-07-23T16:33:35.793Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":130,"output_tokens":2863,"cost_usd":0.040309}
+{"ts":"2026-07-23T16:34:28.252Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":132,"output_tokens":4525,"cost_usd":0.011392}
+{"ts":"2026-07-23T16:36:16.498Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":135,"output_tokens":3819,"cost_usd":0.053702}
+{"ts":"2026-07-23T16:36:48.825Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":342,"output_tokens":5736,"cost_usd":0.087066}
+{"ts":"2026-07-23T16:39:47.103Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":137,"output_tokens":11596,"cost_usd":0.029072}
+{"ts":"2026-07-23T16:40:46.048Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":140,"output_tokens":3033,"cost_usd":0.042707}
+{"ts":"2026-07-23T16:40:54.625Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":344,"output_tokens":3076,"cost_usd":0.047172}
+{"ts":"2026-07-23T16:41:50.367Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":141,"output_tokens":4555,"cost_usd":0.011472}
+{"ts":"2026-07-23T16:44:26.635Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":134,"output_tokens":2752,"cost_usd":0.038762}
+{"ts":"2026-07-23T16:44:33.439Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":342,"output_tokens":3550,"cost_usd":0.054276}
+{"ts":"2026-07-23T16:46:37.144Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":135,"output_tokens":8074,"cost_usd":0.020266}
+{"ts":"2026-07-23T16:47:30.549Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":136,"output_tokens":3089,"cost_usd":0.043484}
+{"ts":"2026-07-23T16:47:44.287Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":342,"output_tokens":2918,"cost_usd":0.044796}
+{"ts":"2026-07-23T16:48:52.862Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":137,"output_tokens":5366,"cost_usd":0.013497}
← ca7402b auto-save: 2026-07-23T09:19:52 (12 files) — data/challenges.
·
back to Model Arena
·
auto-save: 2026-07-23T10:20:16 (23 files) — data/challenges. 543fc51 →