← back to Model Arena
auto-save: 2026-07-23T07:19:17 (50 files) — data/challenges.json data/costlog.jsonl data/votes.jsonl idea-run/create-cycle.sh idea-run/harvest.md
72239ef9817efc09ed2bfcec0b54b91d4ef60460 · 2026-07-23 07:19:24 -0700 · Steve Abrams
Files touched
A data/artifacts/02a362bd0b06/gemma3-12b.htmlA data/artifacts/02a362bd0b06/gemma3-12b.pngA data/artifacts/02a362bd0b06/grok.htmlA data/artifacts/02a362bd0b06/grok.pngA data/artifacts/02a362bd0b06/hermes3-8b.htmlA data/artifacts/02a362bd0b06/hermes3-8b.pngA data/artifacts/02a362bd0b06/kimi.htmlA data/artifacts/02a362bd0b06/kimi.pngA data/artifacts/02a362bd0b06/qwen3-14b.htmlA data/artifacts/02a362bd0b06/qwen3-14b.pngA data/artifacts/0ac1af2eaed4/claude-code.htmlA data/artifacts/0ac1af2eaed4/claude-code.pngA data/artifacts/0ac1af2eaed4/qwen25-7b.htmlA data/artifacts/0ac1af2eaed4/qwen25-7b.pngA data/artifacts/34ecbeae4e1f/gemma3-12b.htmlA data/artifacts/34ecbeae4e1f/gemma3-12b.pngA data/artifacts/34ecbeae4e1f/hermes3-8b.htmlA data/artifacts/34ecbeae4e1f/hermes3-8b.pngA data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.htmlA data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.pngA data/artifacts/34ecbeae4e1f/qwen3-14b.htmlA data/artifacts/34ecbeae4e1f/qwen3-14b.pngA data/artifacts/35303b0ef3b8/gpt.htmlA data/artifacts/35303b0ef3b8/gpt.pngA data/artifacts/35303b0ef3b8/grok.htmlA data/artifacts/35303b0ef3b8/grok.pngA data/artifacts/35303b0ef3b8/hermes3-8b.htmlA data/artifacts/35303b0ef3b8/kimi.htmlA data/artifacts/35303b0ef3b8/kimi.pngA data/artifacts/35303b0ef3b8/qwen25-7b.htmlA data/artifacts/35303b0ef3b8/qwen25-7b.pngA data/artifacts/74df3b61e7ca/qwen25-7b.htmlA data/artifacts/74df3b61e7ca/qwen25-7b.pngA data/artifacts/75ec2d6d47f7/gemma3-12b.htmlA data/artifacts/75ec2d6d47f7/gemma3-12b.pngA data/artifacts/75ec2d6d47f7/gpt.htmlA data/artifacts/75ec2d6d47f7/gpt.pngA data/artifacts/75ec2d6d47f7/grok.htmlA data/artifacts/75ec2d6d47f7/grok.pngA data/artifacts/75ec2d6d47f7/hermes3-8b.htmlA data/artifacts/75ec2d6d47f7/hermes3-8b.pngA data/artifacts/75ec2d6d47f7/kimi.htmlA data/artifacts/75ec2d6d47f7/kimi.pngA data/artifacts/75ec2d6d47f7/qwen25-7b.htmlA data/artifacts/75ec2d6d47f7/qwen25-7b.pngA data/artifacts/75ec2d6d47f7/qwen3-14b.htmlA data/artifacts/75ec2d6d47f7/qwen3-14b.pngA data/artifacts/93a51ee67f87/claude-code.htmlA data/artifacts/93a51ee67f87/claude-code.pngA data/artifacts/966d54f224fd/gpt.htmlA data/artifacts/966d54f224fd/gpt.pngA data/artifacts/966d54f224fd/qwen25-7b.htmlA data/artifacts/966d54f224fd/qwen25-7b.pngA data/artifacts/acebf42d306a/gemma3-12b.htmlA data/artifacts/acebf42d306a/gemma3-12b.pngA data/artifacts/acebf42d306a/grok.htmlA data/artifacts/acebf42d306a/grok.pngA data/artifacts/acebf42d306a/kimi.htmlA data/artifacts/acebf42d306a/kimi.pngA data/artifacts/e068f31235f8/gemma3-12b.htmlA data/artifacts/e068f31235f8/gemma3-12b.pngA data/artifacts/e068f31235f8/hermes3-8b.htmlA data/artifacts/e068f31235f8/hermes3-8b.pngA data/artifacts/e068f31235f8/qwen3-14b.htmlA data/artifacts/e068f31235f8/qwen3-14b.pngA data/artifacts/e14b05b7754d/gpt.htmlA data/artifacts/e14b05b7754d/gpt.pngA data/artifacts/e14b05b7754d/grok.htmlA data/artifacts/e14b05b7754d/grok.pngA data/artifacts/e14b05b7754d/kimi.htmlA data/artifacts/e14b05b7754d/kimi.pngA data/artifacts/e14b05b7754d/qwen25-7b.htmlA data/artifacts/e14b05b7754d/qwen25-7b.pngA data/artifacts/e4acfac81892/gemma3-12b.htmlA data/artifacts/e4acfac81892/gemma3-12b.pngA data/artifacts/e4acfac81892/gpt.htmlA data/artifacts/e4acfac81892/gpt.pngA data/artifacts/e4acfac81892/grok.htmlA data/artifacts/e4acfac81892/grok.pngA data/artifacts/e4acfac81892/hermes3-8b.htmlA data/artifacts/e4acfac81892/hermes3-8b.pngA data/artifacts/e4acfac81892/kimi.htmlA data/artifacts/e4acfac81892/kimi.pngA data/artifacts/e4acfac81892/qwen25-7b.htmlA data/artifacts/e4acfac81892/qwen25-7b.pngA data/artifacts/e4acfac81892/qwen3-14b.htmlA data/artifacts/e4acfac81892/qwen3-14b.pngM data/challenges.jsonM data/costlog.jsonlM data/votes.jsonlM idea-run/create-cycle.shM idea-run/harvest.md
Diff
commit 72239ef9817efc09ed2bfcec0b54b91d4ef60460
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 23 07:19:24 2026 -0700
auto-save: 2026-07-23T07:19:17 (50 files) — data/challenges.json data/costlog.jsonl data/votes.jsonl idea-run/create-cycle.sh idea-run/harvest.md
---
data/artifacts/02a362bd0b06/gemma3-12b.html | 172 +++
data/artifacts/02a362bd0b06/gemma3-12b.png | Bin 0 -> 2728 bytes
data/artifacts/02a362bd0b06/grok.html | 627 +++++++++++
data/artifacts/02a362bd0b06/grok.png | Bin 0 -> 301511 bytes
data/artifacts/02a362bd0b06/hermes3-8b.html | 161 +++
data/artifacts/02a362bd0b06/hermes3-8b.png | Bin 0 -> 2724 bytes
data/artifacts/02a362bd0b06/kimi.html | 354 ++++++
data/artifacts/02a362bd0b06/kimi.png | Bin 0 -> 60770 bytes
data/artifacts/02a362bd0b06/qwen3-14b.html | 101 ++
data/artifacts/02a362bd0b06/qwen3-14b.png | Bin 0 -> 30920 bytes
data/artifacts/0ac1af2eaed4/claude-code.html | 155 +++
data/artifacts/0ac1af2eaed4/claude-code.png | Bin 0 -> 58565 bytes
data/artifacts/0ac1af2eaed4/qwen25-7b.html | 99 ++
data/artifacts/0ac1af2eaed4/qwen25-7b.png | Bin 0 -> 2724 bytes
data/artifacts/34ecbeae4e1f/gemma3-12b.html | 358 ++++++
data/artifacts/34ecbeae4e1f/gemma3-12b.png | Bin 0 -> 55534 bytes
data/artifacts/34ecbeae4e1f/hermes3-8b.html | 268 +++++
data/artifacts/34ecbeae4e1f/hermes3-8b.png | Bin 0 -> 55534 bytes
data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.html | 272 +++++
data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.png | Bin 0 -> 55414 bytes
data/artifacts/34ecbeae4e1f/qwen3-14b.html | 380 +++++++
data/artifacts/34ecbeae4e1f/qwen3-14b.png | Bin 0 -> 56724 bytes
data/artifacts/35303b0ef3b8/gpt.html | 15 +
data/artifacts/35303b0ef3b8/gpt.png | Bin 0 -> 3552 bytes
data/artifacts/35303b0ef3b8/grok.html | 13 +
data/artifacts/35303b0ef3b8/grok.png | Bin 0 -> 5918 bytes
data/artifacts/35303b0ef3b8/hermes3-8b.html | 18 +
data/artifacts/35303b0ef3b8/kimi.html | 29 +
data/artifacts/35303b0ef3b8/kimi.png | Bin 0 -> 7507 bytes
data/artifacts/35303b0ef3b8/qwen25-7b.html | 1 +
data/artifacts/35303b0ef3b8/qwen25-7b.png | Bin 0 -> 2727 bytes
data/artifacts/74df3b61e7ca/qwen25-7b.html | 87 ++
data/artifacts/74df3b61e7ca/qwen25-7b.png | Bin 0 -> 32261 bytes
data/artifacts/75ec2d6d47f7/gemma3-12b.html | 259 +++++
data/artifacts/75ec2d6d47f7/gemma3-12b.png | Bin 0 -> 2728 bytes
data/artifacts/75ec2d6d47f7/gpt.html | 531 +++++++++
data/artifacts/75ec2d6d47f7/gpt.png | Bin 0 -> 170334 bytes
data/artifacts/75ec2d6d47f7/grok.html | 517 +++++++++
data/artifacts/75ec2d6d47f7/grok.png | Bin 0 -> 200899 bytes
data/artifacts/75ec2d6d47f7/hermes3-8b.html | 182 +++
data/artifacts/75ec2d6d47f7/hermes3-8b.png | Bin 0 -> 2724 bytes
data/artifacts/75ec2d6d47f7/kimi.html | 268 +++++
data/artifacts/75ec2d6d47f7/kimi.png | Bin 0 -> 11003 bytes
data/artifacts/75ec2d6d47f7/qwen25-7b.html | 106 ++
data/artifacts/75ec2d6d47f7/qwen25-7b.png | Bin 0 -> 4691 bytes
data/artifacts/75ec2d6d47f7/qwen3-14b.html | 43 +
data/artifacts/75ec2d6d47f7/qwen3-14b.png | Bin 0 -> 2727 bytes
data/artifacts/93a51ee67f87/claude-code.html | 31 +
data/artifacts/93a51ee67f87/claude-code.png | Bin 0 -> 5150 bytes
data/artifacts/966d54f224fd/gpt.html | 332 ++++++
data/artifacts/966d54f224fd/gpt.png | Bin 0 -> 51088 bytes
data/artifacts/966d54f224fd/qwen25-7b.html | 48 +
data/artifacts/966d54f224fd/qwen25-7b.png | Bin 0 -> 2858 bytes
data/artifacts/acebf42d306a/gemma3-12b.html | 176 +++
data/artifacts/acebf42d306a/gemma3-12b.png | Bin 0 -> 123416 bytes
data/artifacts/acebf42d306a/grok.html | 765 +++++++++++++
data/artifacts/acebf42d306a/grok.png | Bin 0 -> 146487 bytes
data/artifacts/acebf42d306a/kimi.html | 1186 ++++++++++++++++++++
data/artifacts/acebf42d306a/kimi.png | Bin 0 -> 62441 bytes
data/artifacts/e068f31235f8/gemma3-12b.html | 103 ++
data/artifacts/e068f31235f8/gemma3-12b.png | Bin 0 -> 13917 bytes
data/artifacts/e068f31235f8/hermes3-8b.html | 95 ++
data/artifacts/e068f31235f8/hermes3-8b.png | Bin 0 -> 54811 bytes
data/artifacts/e068f31235f8/qwen3-14b.html | 126 +++
data/artifacts/e068f31235f8/qwen3-14b.png | Bin 0 -> 10904 bytes
data/artifacts/e14b05b7754d/gpt.html | 493 ++++++++
data/artifacts/e14b05b7754d/gpt.png | Bin 0 -> 18512 bytes
data/artifacts/e14b05b7754d/grok.html | 435 +++++++
data/artifacts/e14b05b7754d/grok.png | Bin 0 -> 31681 bytes
data/artifacts/e14b05b7754d/kimi.html | 409 +++++++
data/artifacts/e14b05b7754d/kimi.png | Bin 0 -> 20380 bytes
data/artifacts/e14b05b7754d/qwen25-7b.html | 113 ++
data/artifacts/e14b05b7754d/qwen25-7b.png | Bin 0 -> 6322 bytes
data/artifacts/e4acfac81892/gemma3-12b.html | 7 +
data/artifacts/e4acfac81892/gemma3-12b.png | Bin 0 -> 3570 bytes
data/artifacts/e4acfac81892/gpt.html | 15 +
data/artifacts/e4acfac81892/gpt.png | Bin 0 -> 4795 bytes
data/artifacts/e4acfac81892/grok.html | 11 +
data/artifacts/e4acfac81892/grok.png | Bin 0 -> 6498 bytes
data/artifacts/e4acfac81892/hermes3-8b.html | 23 +
data/artifacts/e4acfac81892/hermes3-8b.png | Bin 0 -> 4849 bytes
data/artifacts/e4acfac81892/kimi.html | 24 +
data/artifacts/e4acfac81892/kimi.png | Bin 0 -> 8012 bytes
data/artifacts/e4acfac81892/qwen25-7b.html | 1 +
data/artifacts/e4acfac81892/qwen25-7b.png | Bin 0 -> 3562 bytes
data/artifacts/e4acfac81892/qwen3-14b.html | 1 +
data/artifacts/e4acfac81892/qwen3-14b.png | Bin 0 -> 5051 bytes
data/challenges.json | 797 +++++++++----
data/costlog.jsonl | 17 +
data/votes.jsonl | 1 +
idea-run/create-cycle.sh | 2 +-
idea-run/harvest.md | 29 +
92 files changed, 10046 insertions(+), 210 deletions(-)
diff --git a/data/artifacts/02a362bd0b06/gemma3-12b.html b/data/artifacts/02a362bd0b06/gemma3-12b.html
new file mode 100644
index 0000000..2afbbe4
--- /dev/null
+++ b/data/artifacts/02a362bd0b06/gemma3-12b.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Dragon Realm</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: #222; }
+ canvas { display: block; }
+
+ #container {
+ position: relative;
+ width: 100vw;
+ height: 100vh;
+ }
+ </style>
+</head>
+<body>
+ <div id="container">
+ <canvas id="dragonRealm"></canvas>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('dragonRealm');
+ const ctx = canvas.getContext('2d');
+
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ let dragonX = canvas.width * 0.3;
+ let dragonY = canvas.height * 0.4;
+ let dragonSpeed = 1;
+ let dragonAltitude = 50; // How far above the mountains it flies
+
+ const mountainHeightScale = 0.5;
+
+
+ function generateMountains(width) {
+ let mountains = [];
+ for (let i = 0; i < width; i += 60) {
+ let height = Math.random() * canvas.height * mountainHeightScale + 50;
+ mountains.push({ x: i, y: height });
+ }
+ return mountains;
+ }
+
+
+ const mountains = generateMountains(canvas.width);
+
+ let fireActive = false;
+ let fireTimer = 0;
+
+
+
+ function draw() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Background Gradient
+ const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
+ gradient.addColorStop(0, '#111');
+ gradient.addColorStop(1, '#333');
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ // Draw Mountains (with snowfall)
+ ctx.fillStyle = 'rgb(50, 50, 50)'; // Darker mountain color
+ for (const mountain of mountains) {
+ ctx.beginPath();
+ ctx.moveTo(mountain.x, canvas.height - mountain.y);
+ ctx.lineTo(mountain.x + 20, canvas.height - mountain.y + 30);
+ ctx.lineTo(mountain.x + 40, canvas.height - mountain.y);
+ ctx.lineTo(mountain.x + 60, canvas.height - mountain.y + 30);
+ ctx.lineTo(mountain.x + 80, canvas.height - mountain.y);
+ ctx.closePath();
+ ctx.fill();
+
+ //Snowfall on mountains (random dots)
+ if (Math.random() < 0.1) { //Less snowfall on the main part of the mountain
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
+ ctx.beginPath();
+ ctx.arc(mountain.x + Math.random() * 80 , canvas.height - (mountain.y + Math.random()*10) , 2, 0, 2*Math.PI);
+ ctx.fill();
+ }
+
+ }
+
+
+ // Draw Dragon
+ ctx.save(); // Save current state
+ ctx.translate(dragonX, dragonY);
+
+ ctx.fillStyle = 'green';
+ ctx.beginPath();
+ ctx.arc(0, -dragonAltitude , 30, 0, 2 * Math.PI);
+ ctx.fill();
+
+
+ ctx.fillStyle = 'darkgreen';
+ ctx.beginPath();
+ ctx.arc(-15,-dragonAltitude + 15, 15, 0, 2 * Math.PI);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.arc(15,-dragonAltitude+15, 15, 0, 2 * Math.PI);
+ ctx.fill();
+
+
+ ctx.restore(); // Restore previous state
+
+
+
+ // Draw Fire (if active)
+ if (fireActive) {
+ ctx.fillStyle = 'orange';
+ ctx.beginPath();
+ ctx.arc(dragonX + 40, dragonY - dragonAltitude , 20, 0, 2 * Math.PI);
+ ctx.fill();
+ fireTimer--;
+
+ if (fireTimer <= 0) {
+ fireActive = false;
+ }
+ }
+
+
+ //Snowfall overall
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
+ for (let i = 0; i < canvas.width; i++) {
+ ctx.beginPath();
+ ctx.arc(i , canvas.height - Math.random() * 10 - 3, 1, 0, 2*Math.PI);
+ ctx.fill();
+ }
+
+
+ // Update Dragon Position
+ dragonX += dragonSpeed;
+ if (dragonX > canvas.width) {
+ dragonX = 0; // Loop around
+ }
+
+ }
+
+
+
+ function handleClick(event) {
+ if (event.clientX >= dragonX - 20 && event.clientX <= dragonX + 60 &&
+ event.clientY >= dragonY - dragonAltitude -40 && event.clientY <= dragonY - dragonAltitude+20 ) {
+
+ fireActive = true;
+ fireTimer = 30; // Duration of fire effect
+ }
+ }
+
+
+ canvas.addEventListener('click', handleClick);
+
+ draw(); // Initial draw
+ setInterval(draw, 30); // Draw every 30ms (approx. 33fps)
+
+ window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ mountains.length = 0;
+ const newMountains = generateMountains(canvas.width);
+ mountains.push(...newMountains);
+
+
+ });
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/02a362bd0b06/gemma3-12b.png b/data/artifacts/02a362bd0b06/gemma3-12b.png
new file mode 100644
index 0000000..20d4ae2
Binary files /dev/null and b/data/artifacts/02a362bd0b06/gemma3-12b.png differ
diff --git a/data/artifacts/02a362bd0b06/grok.html b/data/artifacts/02a362bd0b06/grok.html
new file mode 100644
index 0000000..aa77188
--- /dev/null
+++ b/data/artifacts/02a362bd0b06/grok.html
@@ -0,0 +1,627 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Dragon Realm</title>
+<style>
+*{margin:0;padding:0;box-sizing:border-box}
+html,body{width:100%;height:100%;overflow:hidden;background:#0a0a12;cursor:crosshair}
+canvas{display:block;width:100%;height:100%}
+#hint{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);color:rgba(255,220,180,0.7);font:14px/1.4 Georgia,serif;letter-spacing:0.12em;text-transform:uppercase;pointer-events:none;text-shadow:0 0 12px rgba(255,140,60,0.5);transition:opacity 1.5s;z-index:10}
+#hint.hide{opacity:0}
+</style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<div id="hint">Click to summon dragonfire</div>
+<script>
+const canvas=document.getElementById('c');
+const ctx=canvas.getContext('2d');
+let W,H,dpr;
+function resize(){
+ dpr=Math.min(window.devicePixelRatio||1,2);
+ W=window.innerWidth;H=window.innerHeight;
+ canvas.width=W*dpr;canvas.height=H*dpr;
+ ctx.setTransform(dpr,0,0,dpr,0,0);
+}
+resize();
+window.addEventListener('resize',resize);
+
+// ─── Utilities ───
+const rand=(a,b)=>a+Math.random()*(b-a);
+const clamp=(v,a,b)=>Math.max(a,Math.min(b,v));
+const lerp=(a,b,t)=>a+(b-a)*t;
+
+// ─── Stars ───
+const stars=Array.from({length:180},()=>({
+ x:Math.random(),y:Math.random()*0.7,
+ r:rand(0.4,1.8),ph:Math.random()*Math.PI*2,
+ sp:rand(0.5,2.5),b:rand(0.4,1)
+}));
+
+// ─── Snow ───
+const snow=Array.from({length:220},()=>({
+ x:Math.random(),y:Math.random(),
+ r:rand(0.8,3.2),sp:rand(0.15,0.7),
+ drift:rand(-0.15,0.15),ph:Math.random()*Math.PI*2,
+ a:rand(0.35,0.9)
+}));
+
+// ─── Fire particles ───
+let flames=[];
+
+// ─── Mountains (layered silhouettes) ───
+function genMountains(baseY,amp,freq,rough,seed){
+ const pts=[];
+ let x=0;
+ let y=baseY;
+ let s=seed;
+ while(x<=1.05){
+ s=Math.sin(s*127.1+311.7)*43758.5453;
+ const n=s-Math.floor(s);
+ y=baseY-Math.abs(Math.sin(x*freq*Math.PI*2+seed))*amp
+ -n*rough*amp
+ -Math.sin(x*freq*3.7+seed*2)*amp*0.25;
+ pts.push({x,y});
+ x+=0.012+n*0.008;
+ }
+ return pts;
+}
+const mtnFar=genMountains(0.58,0.14,1.8,0.35,1.1);
+const mtnMid=genMountains(0.68,0.20,2.4,0.45,2.7);
+const mtnNear=genMountains(0.82,0.22,3.1,0.55,4.3);
+
+function drawMountains(pts,fill,glow){
+ ctx.beginPath();
+ ctx.moveTo(0,H);
+ for(const p of pts)ctx.lineTo(p.x*W,p.y*H);
+ ctx.lineTo(W,H);
+ ctx.closePath();
+ if(glow){
+ ctx.shadowColor=glow;
+ ctx.shadowBlur=30;
+ }
+ ctx.fillStyle=fill;
+ ctx.fill();
+ ctx.shadowBlur=0;
+}
+
+// ─── Aurora ribbons ───
+function drawAurora(t){
+ ctx.save();
+ ctx.globalCompositeOperation='screen';
+ for(let i=0;i<4;i++){
+ const y0=H*0.08+i*H*0.06;
+ const grad=ctx.createLinearGradient(0,y0,W,y0+H*0.15);
+ const hue=160+i*25+Math.sin(t*0.3+i)*20;
+ grad.addColorStop(0,`hsla(${hue},80%,55%,0)`);
+ grad.addColorStop(0.3,`hsla(${hue},90%,60%,${0.08+Math.sin(t*0.5+i)*0.04})`);
+ grad.addColorStop(0.55,`hsla(${hue+30},85%,65%,${0.12+Math.sin(t*0.4+i*1.3)*0.05})`);
+ grad.addColorStop(0.8,`hsla(${hue+50},80%,55%,0.06)`);
+ grad.addColorStop(1,`hsla(${hue},70%,50%,0)`);
+ ctx.fillStyle=grad;
+ ctx.beginPath();
+ ctx.moveTo(0,y0);
+ for(let x=0;x<=W;x+=8){
+ const nx=x/W;
+ const y=y0
+ +Math.sin(nx*4+t*0.4+i)*H*0.04
+ +Math.sin(nx*9+t*0.7+i*2)*H*0.02
+ +Math.sin(nx*2.5-t*0.25)*H*0.03;
+ ctx.lineTo(x,y);
+ }
+ for(let x=W;x>=0;x-=8){
+ const nx=x/W;
+ const y=y0+H*0.12
+ +Math.sin(nx*3.5+t*0.35+i)*H*0.05
+ +Math.cos(nx*7+t*0.55)*H*0.025;
+ ctx.lineTo(x,y);
+ }
+ ctx.closePath();
+ ctx.fill();
+ }
+ ctx.restore();
+}
+
+// ─── Moon ───
+function drawMoon(t){
+ const mx=W*0.78,my=H*0.16,r=Math.min(W,H)*0.055;
+ // glow
+ const g=ctx.createRadialGradient(mx,my,r*0.3,mx,my,r*4);
+ g.addColorStop(0,'rgba(255,240,210,0.25)');
+ g.addColorStop(0.4,'rgba(200,210,255,0.08)');
+ g.addColorStop(1,'rgba(180,200,255,0)');
+ ctx.fillStyle=g;
+ ctx.beginPath();ctx.arc(mx,my,r*4,0,Math.PI*2);ctx.fill();
+ // body
+ const mg=ctx.createRadialGradient(mx-r*0.3,my-r*0.3,r*0.1,mx,my,r);
+ mg.addColorStop(0,'#fff8e8');
+ mg.addColorStop(0.6,'#e8e0d0');
+ mg.addColorStop(1,'#c8c0b0');
+ ctx.fillStyle=mg;
+ ctx.beginPath();ctx.arc(mx,my,r,0,Math.PI*2);ctx.fill();
+ // craters
+ ctx.fillStyle='rgba(160,150,140,0.25)';
+ [[0.3,-0.2,0.18],[ -0.25,0.25,0.12],[0.1,0.35,0.1],[-0.35,-0.15,0.08]].forEach(([cx,cy,cr])=>{
+ ctx.beginPath();ctx.arc(mx+cx*r,my+cy*r,cr*r,0,Math.PI*2);ctx.fill();
+ });
+}
+
+// ─── Dragon ───
+const dragon={
+ x:0.2,y:0.38,
+ vx:0.00035,phase:0,
+ wing:0,breathing:0,breathT:0,
+ scale:1,dir:1
+};
+
+function dragonWorld(){
+ return {
+ x:dragon.x*W,
+ y:dragon.y*H+Math.sin(dragon.phase)*H*0.03,
+ s:Math.min(W,H)*0.0011*dragon.scale
+ };
+}
+
+function drawDragonBody(t){
+ const {x,y,s}=dragonWorld();
+ const wingAng=Math.sin(dragon.wing)*0.7;
+ const wingAng2=Math.sin(dragon.wing+0.4)*0.5;
+ ctx.save();
+ ctx.translate(x,y);
+ ctx.scale(dragon.dir*s,s);
+
+ // tail
+ ctx.strokeStyle='#1a3a2a';
+ ctx.lineWidth=14;
+ ctx.lineCap='round';
+ ctx.beginPath();
+ ctx.moveTo(-40,10);
+ for(let i=1;i<=12;i++){
+ const tx=-40-i*22;
+ const ty=10+Math.sin(dragon.phase*2+i*0.55)*18*i*0.12+i*i*0.15;
+ ctx.lineTo(tx,ty);
+ }
+ ctx.stroke();
+ // tail spikes
+ ctx.fillStyle='#2a5a3a';
+ for(let i=2;i<=11;i+=2){
+ const tx=-40-i*22;
+ const ty=10+Math.sin(dragon.phase*2+i*0.55)*18*i*0.12+i*i*0.15;
+ const nx=-22,ny=Math.sin(dragon.phase*2+(i+1)*0.55)*18*(i+1)*0.12+(i+1)*(i+1)*0.15
+ -(Math.sin(dragon.phase*2+i*0.55)*18*i*0.12+i*i*0.15);
+ ctx.beginPath();
+ ctx.moveTo(tx,ty-8);
+ ctx.lineTo(tx-8,ty-28);
+ ctx.lineTo(tx+nx*0.3,ty+ny*0.3);
+ ctx.fill();
+ }
+ // tail tip flame-ish glow when breathing
+ if(dragon.breathing>0){
+ const tipX=-40-12*22,tipY=10+Math.sin(dragon.phase*2+12*0.55)*18*12*0.12+144*0.15;
+ ctx.fillStyle=`rgba(255,120,40,${dragon.breathing*0.3})`;
+ ctx.beginPath();ctx.arc(tipX,tipY,20,0,Math.PI*2);ctx.fill();
+ }
+
+ // back wing (far)
+ ctx.save();
+ ctx.translate(-10,-5);
+ ctx.rotate(-wingAng2*0.8-0.3);
+ drawWing(-1);
+ ctx.restore();
+
+ // body
+ const bodyGrad=ctx.createLinearGradient(0,-40,0,40);
+ bodyGrad.addColorStop(0,'#3d8b5e');
+ bodyGrad.addColorStop(0.4,'#2a6b45');
+ bodyGrad.addColorStop(1,'#1a3a28');
+ ctx.fillStyle=bodyGrad;
+ ctx.beginPath();
+ ctx.ellipse(10,5,55,28, -0.15,0,Math.PI*2);
+ ctx.fill();
+ // belly
+ ctx.fillStyle='#4a7a55';
+ ctx.beginPath();
+ ctx.ellipse(15,14,40,16,-0.15,0,Math.PI*2);
+ ctx.fill();
+ // scales hint
+ ctx.strokeStyle='rgba(20,60,35,0.35)';
+ ctx.lineWidth=1.5;
+ for(let i=-2;i<=3;i++){
+ ctx.beginPath();
+ ctx.arc(i*14,0,10,0.2,Math.PI-0.2);
+ ctx.stroke();
+ }
+
+ // neck
+ ctx.fillStyle='#2d7048';
+ ctx.beginPath();
+ ctx.moveTo(50,-5);
+ ctx.quadraticCurveTo(85,-35,105,-28);
+ ctx.quadraticCurveTo(115,-20,110,-5);
+ ctx.quadraticCurveTo(90,10,55,15);
+ ctx.closePath();
+ ctx.fill();
+
+ // head
+ ctx.fillStyle='#348050';
+ ctx.beginPath();
+ ctx.ellipse(125,-22,28,18,-0.2,0,Math.PI*2);
+ ctx.fill();
+ // snout
+ ctx.beginPath();
+ ctx.moveTo(140,-28);
+ ctx.quadraticCurveTo(175,-30,180,-18);
+ ctx.quadraticCurveTo(175,-8,145,-10);
+ ctx.closePath();
+ ctx.fill();
+ // jaw
+ const jawOpen=dragon.breathing*0.35;
+ ctx.fillStyle='#2a6040';
+ ctx.beginPath();
+ ctx.moveTo(145,-12);
+ ctx.quadraticCurveTo(170,-8+jawOpen*40,178,-14+jawOpen*30);
+ ctx.quadraticCurveTo(165,0+jawOpen*20,140,-5);
+ ctx.closePath();
+ ctx.fill();
+ // horns
+ ctx.fillStyle='#1a3020';
+ ctx.beginPath();
+ ctx.moveTo(115,-35);
+ ctx.quadraticCurveTo(108,-60,100,-72);
+ ctx.quadraticCurveTo(112,-55,122,-36);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.moveTo(128,-38);
+ ctx.quadraticCurveTo(130,-65,125,-80);
+ ctx.quadraticCurveTo(138,-60,135,-36);
+ ctx.fill();
+ // eye
+ ctx.fillStyle='#1a1008';
+ ctx.beginPath();ctx.ellipse(135,-26,6,5,0,0,Math.PI*2);ctx.fill();
+ const eyeGlow=0.5+dragon.breathing*0.5;
+ ctx.fillStyle=`rgba(255,200,50,${eyeGlow})`;
+ ctx.shadowColor='#ffaa00';
+ ctx.shadowBlur=8+dragon.breathing*12;
+ ctx.beginPath();ctx.ellipse(136,-26,3.5,4,0,0,Math.PI*2);ctx.fill();
+ ctx.shadowBlur=0;
+ // nostril
+ ctx.fillStyle='#1a3020';
+ ctx.beginPath();ctx.arc(168,-22,3,0,Math.PI*2);ctx.fill();
+
+ // front wing
+ ctx.save();
+ ctx.translate(5,-10);
+ ctx.rotate(-wingAng-0.2);
+ drawWing(1);
+ ctx.restore();
+
+ // legs
+ ctx.strokeStyle='#1a3a28';
+ ctx.lineWidth=8;
+ ctx.lineCap='round';
+ // rear
+ ctx.beginPath();
+ ctx.moveTo(-15,20);
+ ctx.quadraticCurveTo(-20,45,-10,55+Math.sin(dragon.phase*1.5)*4);
+ ctx.stroke();
+ // front
+ ctx.beginPath();
+ ctx.moveTo(30,22);
+ ctx.quadraticCurveTo(40,48,35,58+Math.sin(dragon.phase*1.5+1)*4);
+ ctx.stroke();
+
+ // spine ridges
+ ctx.fillStyle='#1e4a32';
+ for(let i=-1;i<=3;i++){
+ ctx.beginPath();
+ ctx.moveTo(i*16-5,-18);
+ ctx.lineTo(i*16,-38-Math.abs(i)*2);
+ ctx.lineTo(i*16+8,-16);
+ ctx.fill();
+ }
+
+ ctx.restore();
+
+ // fire breath origin in world coords for particles
+ return {
+ fx:x+dragon.dir*s*(180),
+ fy:y+s*(-18+jawOpen*10),
+ dir:dragon.dir
+ };
+}
+
+function drawWing(side){
+ // membrane
+ const wg=ctx.createLinearGradient(0,0,0,120);
+ wg.addColorStop(0,'rgba(60,120,80,0.85)');
+ wg.addColorStop(0.5,'rgba(30,70,50,0.75)');
+ wg.addColorStop(1,'rgba(15,40,30,0.5)');
+ ctx.fillStyle=wg;
+ ctx.beginPath();
+ ctx.moveTo(0,0);
+ ctx.quadraticCurveTo(40*side,-30,90*side,-10);
+ ctx.quadraticCurveTo(120*side,30,100*side,90);
+ ctx.quadraticCurveTo(60*side,70,30*side,100);
+ ctx.quadraticCurveTo(20*side,50,0,10);
+ ctx.closePath();
+ ctx.fill();
+ // bone structure
+ ctx.strokeStyle='rgba(20,50,30,0.7)';
+ ctx.lineWidth=3;
+ ctx.beginPath();
+ ctx.moveTo(0,0);
+ ctx.lineTo(90*side,-10);
+ ctx.moveTo(0,0);
+ ctx.lineTo(100*side,90);
+ ctx.moveTo(0,0);
+ ctx.lineTo(30*side,100);
+ ctx.moveTo(40*side,-5);
+ ctx.lineTo(70*side,50);
+ ctx.stroke();
+ // membrane glow edge
+ ctx.strokeStyle='rgba(100,200,140,0.25)';
+ ctx.lineWidth=1.5;
+ ctx.beginPath();
+ ctx.moveTo(90*side,-10);
+ ctx.quadraticCurveTo(120*side,30,100*side,90);
+ ctx.quadraticCurveTo(60*side,70,30*side,100);
+ ctx.stroke();
+}
+
+// ─── Fire system ───
+function spawnFire(fx,fy,dir){
+ const n=18+Math.floor(Math.random()*12);
+ for(let i=0;i<n;i++){
+ const speed=rand(3,11);
+ const spread=rand(-0.35,0.35);
+ flames.push({
+ x:fx,y:fy,
+ vx:dir*speed*Math.cos(spread)+rand(-0.5,0.5),
+ vy:speed*Math.sin(spread)*0.6+rand(-1.5,0.5),
+ life:1,
+ decay:rand(0.012,0.028),
+ r:rand(4,16),
+ hue:rand(15,45),
+ type:Math.random()<0.3?1:0
+ });
+ }
+ // smoke
+ for(let i=0;i<4;i++){
+ flames.push({
+ x:fx,y:fy,
+ vx:dir*rand(1,3),
+ vy:rand(-2,-0.5),
+ life:1,
+ decay:rand(0.008,0.015),
+ r:rand(10,25),
+ hue:0,
+ type:2
+ });
+ }
+}
+
+function updateFlames(){
+ for(let i=flames.length-1;i>=0;i--){
+ const f=flames[i];
+ f.x+=f.vx;
+ f.y+=f.vy;
+ f.vy-=0.04;
+ f.vx*=0.99;
+ f.life-=f.decay;
+ f.r*=0.985;
+ if(f.life<=0||f.r<0.5)flames.splice(i,1);
+ }
+}
+
+function drawFlames(){
+ ctx.save();
+ ctx.globalCompositeOperation='lighter';
+ for(const f of flames){
+ if(f.type===2){
+ ctx.globalCompositeOperation='source-over';
+ ctx.fillStyle=`rgba(60,55,50,${f.life*0.2})`;
+ ctx.beginPath();ctx.arc(f.x,f.y,f.r*(2-f.life),0,Math.PI*2);ctx.fill();
+ ctx.globalCompositeOperation='lighter';
+ continue;
+ }
+ const a=f.life;
+ const g=ctx.createRadialGradient(f.x,f.y,0,f.x,f.y,f.r);
+ if(f.type===1){
+ g.addColorStop(0,`rgba(255,255,220,${a})`);
+ g.addColorStop(0.3,`rgba(255,200,50,${a*0.8})`);
+ g.addColorStop(0.7,`rgba(255,100,20,${a*0.4})`);
+ g.addColorStop(1,`rgba(255,40,0,0)`);
+ }else{
+ g.addColorStop(0,`rgba(255,240,180,${a*0.9})`);
+ g.addColorStop(0.25,`hsla(${f.hue},100%,55%,${a*0.7})`);
+ g.addColorStop(0.6,`hsla(${f.hue-10},100%,40%,${a*0.35})`);
+ g.addColorStop(1,`hsla(${f.hue},100%,30%,0)`);
+ }
+ ctx.fillStyle=g;
+ ctx.beginPath();ctx.arc(f.x,f.y,f.r,0,Math.PI*2);ctx.fill();
+ }
+ ctx.restore();
+}
+
+// ─── Ground mist ───
+function drawMist(t){
+ ctx.save();
+ ctx.globalCompositeOperation='screen';
+ for(let i=0;i<5;i++){
+ const gy=H*0.75+i*H*0.05;
+ const gg=ctx.createLinearGradient(0,gy-40,0,gy+60);
+ gg.addColorStop(0,'rgba(100,140,180,0)');
+ gg.addColorStop(0.5,`rgba(120,160,200,${0.04+Math.sin(t*0.3+i)*0.02})`);
+ gg.addColorStop(1,'rgba(80,120,160,0)');
+ ctx.fillStyle=gg;
+ ctx.beginPath();
+ ctx.moveTo(0,gy);
+ for(let x=0;x<=W;x+=20){
+ ctx.lineTo(x,gy+Math.sin(x*0.01+t*0.4+i*1.5)*15+Math.sin(x*0.003-t*0.2)*25);
+ }
+ ctx.lineTo(W,H);ctx.lineTo(0,H);ctx.closePath();
+ ctx.fill();
+ }
+ ctx.restore();
+}
+
+// ─── Lighting overlay ───
+let fireLight=0;
+function drawLighting(t,mouth){
+ // ambient night vignette
+ const vig=ctx.createRadialGradient(W*0.5,H*0.3,H*0.1,W*0.5,H*0.4,H*0.9);
+ vig.addColorStop(0,'rgba(0,0,0,0)');
+ vig.addColorStop(0.7,'rgba(0,0,10,0.15)');
+ vig.addColorStop(1,'rgba(0,0,20,0.55)');
+ ctx.fillStyle=vig;
+ ctx.fillRect(0,0,W,H);
+
+ // fire dynamic light
+ if(fireLight>0.01&&mouth){
+ const lg=ctx.createRadialGradient(mouth.fx,mouth.fy,10,mouth.fx,mouth.fy,280*fireLight);
+ lg.addColorStop(0,`rgba(255,180,60,${0.2*fireLight})`);
+ lg.addColorStop(0.3,`rgba(255,100,20,${0.1*fireLight})`);
+ lg.addColorStop(0.7,`rgba(255,50,0,${0.04*fireLight})`);
+ lg.addColorStop(1,'rgba(0,0,0,0)');
+ ctx.fillStyle=lg;
+ ctx.globalCompositeOperation='screen';
+ ctx.fillRect(0,0,W,H);
+ ctx.globalCompositeOperation='source-over';
+ }
+}
+
+// ─── Interaction ───
+let hintHidden=false;
+canvas.addEventListener('click',(e)=>{
+ dragon.breathing=1;
+ dragon.breathT=0;
+ if(!hintHidden){
+ document.getElementById('hint').classList.add('hide');
+ hintHidden=true;
+ }
+});
+canvas.addEventListener('touchstart',(e)=>{
+ e.preventDefault();
+ dragon.breathing=1;
+ dragon.breathT=0;
+ if(!hintHidden){
+ document.getElementById('hint').classList.add('hide');
+ hintHidden=true;
+ }
+},{passive:false});
+
+// ─── Main loop ───
+let t0=performance.now();
+function frame(now){
+ const t=(now-t0)/1000;
+ const dt=Math.min((now-(frame.prev||now))/1000,0.05);
+ frame.prev=now;
+
+ // sky
+ const sky=ctx.createLinearGradient(0,0,0,H);
+ sky.addColorStop(0,'#06061a');
+ sky.addColorStop(0.3,'#0c1030');
+ sky.addColorStop(0.55,'#152045');
+ sky.addColorStop(0.75,'#1a2840');
+ sky.addColorStop(1,'#0e1a28');
+ ctx.fillStyle=sky;
+ ctx.fillRect(0,0,W,H);
+
+ // stars
+ for(const s of stars){
+ const tw=0.5+0.5*Math.sin(t*s.sp+s.ph);
+ ctx.fillStyle=`rgba(220,230,255,${s.b*tw})`;
+ ctx.beginPath();
+ ctx.arc(s.x*W,s.y*H,s.r*(0.7+tw*0.3),0,Math.PI*2);
+ ctx.fill();
+ }
+
+ drawAurora(t);
+ drawMoon(t);
+
+ // far mountains
+ drawMountains(mtnFar,'#12182a','rgba(80,120,180,0.15)');
+ // mid
+ drawMountains(mtnMid,'#0e1520','rgba(60,100,140,0.1)');
+
+ // snow behind dragon
+ for(const p of snow){
+ if(p.r>2)continue;
+ p.y+=p.sp*dt*0.25;
+ p.x+=p.drift*dt*0.15+Math.sin(t*1.5+p.ph)*0.0003;
+ if(p.y>1){p.y=-0.02;p.x=Math.random()}
+ if(p.x<0)p.x+=1;if(p.x>1)p.x-=1;
+ ctx.fillStyle=`rgba(220,230,255,${p.a*0.5})`;
+ ctx.beginPath();ctx.arc(p.x*W,p.y*H,p.r,0,Math.PI*2);ctx.fill();
+ }
+
+ // dragon update
+ dragon.phase+=dt*1.4;
+ dragon.wing+=dt*5.5;
+ dragon.x+=dragon.vx*60*dt;
+ if(dragon.x>1.25){dragon.x=-0.25;dragon.y=rand(0.28,0.48)}
+ // gentle bob already in dragonWorld
+
+ if(dragon.breathing>0){
+ dragon.breathT+=dt;
+ dragon.breathing=Math.max(0,1-dragon.breathT/1.8);
+ fireLight=lerp(fireLight,dragon.breathing,0.15);
+ if(dragon.breathT<1.4){
+ const m=dragonWorld();
+ const jawOpen=dragon.breathing*0.35;
+ spawnFire(
+ m.x+dragon.dir*m.s*180,
+ m.y+m.s*(-18),
+ dragon.dir
+ );
+ }
+ }else{
+ fireLight=lerp(fireLight,0,0.08);
+ }
+
+ updateFlames();
+ const mouth=drawDragonBody(t);
+ drawFlames();
+
+ // near mountains
+ drawMountains(mtnNear,'#0a1018',null);
+
+ // foreground snow
+ for(const p of snow){
+ if(p.r<=2)continue;
+ p.y+=p.sp*dt*0.35;
+ p.x+=p.drift*dt*0.2+Math.sin(t*1.2+p.ph)*0.0004;
+ if(p.y>1){p.y=-0.02;p.x=Math.random()}
+ if(p.x<0)p.x+=1;if(p.x>1)p.x-=1;
+ ctx.fillStyle=`rgba(230,235,255,${p.a})`;
+ ctx.beginPath();ctx.arc(p.x*W,p.y*H,p.r,0,Math.PI*2);ctx.fill();
+ // sparkle
+ if(p.r>2.5&&Math.sin(t*3+p.ph)>0.7){
+ ctx.fillStyle=`rgba(255,255,255,${p.a*0.6})`;
+ ctx.fillRect(p.x*W-0.5,p.y*H-p.r*1.5,1,p.r*3);
+ ctx.fillRect(p.x*W-p.r*1.5,p.y*H-0.5,p.r*3,1);
+ }
+ }
+
+ drawMist(t);
+ drawLighting(t,mouth);
+
+ // snowy ground highlight on near peaks
+ ctx.strokeStyle='rgba(180,200,220,0.12)';
+ ctx.lineWidth=2;
+ ctx.beginPath();
+ let first=true;
+ for(const p of mtnNear){
+ if(first){ctx.moveTo(p.x*W,p.y*H);first=false}
+ else ctx.lineTo(p.x*W,p.y*H);
+ }
+ ctx.stroke();
+
+ requestAnimationFrame(frame);
+}
+requestAnimationFrame(frame);
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/02a362bd0b06/grok.png b/data/artifacts/02a362bd0b06/grok.png
new file mode 100644
index 0000000..d5bd0f9
Binary files /dev/null and b/data/artifacts/02a362bd0b06/grok.png differ
diff --git a/data/artifacts/02a362bd0b06/hermes3-8b.html b/data/artifacts/02a362bd0b06/hermes3-8b.html
new file mode 100644
index 0000000..853a909
--- /dev/null
+++ b/data/artifacts/02a362bd0b06/hermes3-8b.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Animated Dragon Realm</title>
+ <style>
+ body {
+ background-color: #000;
+ margin: 0;
+ overflow: hidden;
+ }
+
+ #scene {
+ width: 100vw;
+ height: 100vh;
+ perspective: 800px;
+ }
+
+ .mountain {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ right: 0;
+ transform: translateY(-50%) translateZ(600px);
+ background-color: #9e7a48;
+ }
+
+ .peak1, .peak2 {
+ position: absolute;
+ top: -100vh;
+ width: 10vw;
+ height: 20vh;
+ background: linear-gradient(to bottom, rgba(158,122,72,1), rgba(0,0,0,0));
+ }
+
+ .peak1 { right: 45%; }
+ .peak2 { left: 45%; }
+
+ #dragon {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%) translateZ(400px);
+ cursor: pointer;
+ }
+
+ .wing {
+ position: absolute;
+ top: -100vh;
+ width: 18vw;
+ height: 40vh;
+ background-color: #fff;
+ box-shadow: 0 30px 60px rgba(255,255,255,.5);
+ }
+
+ .body {
+ position: absolute;
+ bottom: 10vh;
+ left: 50%;
+ width: 20vw;
+ height: 40vh;
+ background-color: #9e7a48;
+ transform: translateX(-50%);
+ border-radius: 50%;
+ }
+
+ .tail {
+ position: absolute;
+ bottom: 5vh;
+ right: -15vw;
+ width: 30vw;
+ height: 10vh;
+ background-color: #9e7a48;
+ transform: rotate(145deg);
+ }
+
+ .fire {
+ display: none;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ border-left: 20px solid transparent;
+ border-right: 20px solid transparent;
+ border-bottom: 40px solid #ff5f1e;
+ transform: translateX(-50%) translateY(-50%);
+ }
+
+ .snowflake {
+ position: absolute;
+ top: 0;
+ width: 10vw;
+ height: 15vh;
+ background-color: #fff;
+ border-radius: 50%;
+ box-shadow: 0 30px 60px rgba(255,255,255,.2);
+ }
+
+ .snowflake:nth-child(even) {
+ transform: translateX(-50%) translateY(-200vh);
+ }
+
+ .snowflake:nth-child(odd) {
+ transform: translateX(50%) translateY(-200vh);
+ }
+
+ #lightning {
+ display: none;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ border-left: 40px solid transparent;
+ border-right: 40px solid transparent;
+ border-bottom: 80px solid #ffd700;
+ transform: translateX(-50%) translateY(-50%);
+ }
+
+ </style>
+</head>
+<body>
+ <div id="scene">
+ <div class="mountain peak1"></div>
+ <div class="mountain peak2"></div>
+ <div id="dragon">
+ <div class="wing wing-left"></div>
+ <div class="wing wing-right"></div>
+ <div class="body"></div>
+ <div class="tail"></div>
+ <div class="fire"></div>
+ </div>
+ <?php for ($i = 0; $i < 20; $i++): ?>
+ <div class="snowflake snowflake-{{$i}}'></div>
+ <?php endfor; ?>
+ </div>
+
+ <script>
+ document.addEventListener('DOMContentLoaded', function() {
+ const dragon = document.getElementById('dragon');
+ let fireTimer = null;
+
+ function toggleFire() {
+ const fire = document.querySelector('.fire');
+ fire.style.display = fire.style.display === 'none' ? 'block' : 'none';
+
+ if (fireTimer) clearTimeout(fireTimer);
+ else fireTimer = setTimeout(function() {
+ toggleFire();
+ }, 1000);
+ }
+
+ dragon.addEventListener('click', function() {
+ document.querySelector('.fire').style.display = 'block';
+ toggleFire();
+ });
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/02a362bd0b06/hermes3-8b.png b/data/artifacts/02a362bd0b06/hermes3-8b.png
new file mode 100644
index 0000000..ad57eb4
Binary files /dev/null and b/data/artifacts/02a362bd0b06/hermes3-8b.png differ
diff --git a/data/artifacts/02a362bd0b06/kimi.html b/data/artifacts/02a362bd0b06/kimi.html
new file mode 100644
index 0000000..14c8c32
--- /dev/null
+++ b/data/artifacts/02a362bd0b06/kimi.html
@@ -0,0 +1,354 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Dragon Realm</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #000;
+ }
+ canvas {
+ display: block;
+ cursor: crosshair;
+ }
+ </style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<script>
+const canvas = document.getElementById('c');
+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 rand = (min, max) => Math.random() * (max - min) + min;
+
+class SnowParticle {
+ constructor() {
+ this.reset();
+ this.y = rand(0, height);
+ }
+ reset() {
+ this.x = rand(0, width);
+ this.y = -10;
+ this.size = rand(1, 3.5);
+ this.speed = rand(1, 4);
+ this.drift = rand(-0.5, 0.5);
+ this.opacity = rand(0.4, 0.9);
+ }
+ update() {
+ this.y += this.speed;
+ this.x += this.drift + Math.sin(this.y * 0.01) * 0.5;
+ if (this.y > height) this.reset();
+ }
+ draw() {
+ ctx.fillStyle = `rgba(255,255,255,${this.opacity})`;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI*2);
+ ctx.fill();
+ }
+}
+
+class FireParticle {
+ constructor(x, y, angle, intensity) {
+ this.x = x;
+ this.y = y;
+ const speed = rand(3, 8) * intensity;
+ const spread = rand(-0.4, 0.4);
+ this.vx = Math.cos(angle + spread) * speed;
+ this.vy = Math.sin(angle + spread) * speed;
+ this.life = 1;
+ this.decay = rand(0.015, 0.035);
+ this.size = rand(8, 20);
+ this.grow = 0.3;
+ this.type = Math.random() > 0.7 ? 'smoke' : 'fire';
+ }
+ update() {
+ this.x += this.vx;
+ this.y += this.vy;
+ this.vy += 0.08;
+ this.size += this.grow;
+ this.life -= this.decay;
+ if (this.type === 'smoke') {
+ this.vx *= 0.98;
+ this.vy *= 0.98;
+ }
+ }
+ draw() {
+ if (this.type === 'fire') {
+ const r = 255;
+ const g = Math.floor(255 * this.life);
+ const b = Math.floor(100 * this.life * this.life);
+ const a = this.life;
+ ctx.fillStyle = `rgba(${r},${g},${b},${a})`;
+ } else {
+ const c = Math.floor(100 * (1-this.life) + 50);
+ ctx.fillStyle = `rgba(${c},${c},${c},${this.life * 0.3})`;
+ }
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI*2);
+ ctx.fill();
+ }
+}
+
+class MountainLayer {
+ constructor(color, speed, amplitude, yOffset, complexity) {
+ this.color = color;
+ this.speed = speed;
+ this.offset = rand(0, 10000);
+ this.amplitude = amplitude;
+ this.yOffset = yOffset;
+ this.complexity = complexity;
+ }
+
+ getHeight(x) {
+ let y = 0;
+ for(let i=0; i<this.complexity.length; i++) {
+ const freq = this.complexity[i][0];
+ const amp = this.complexity[i][1];
+ y += Math.sin((x + this.offset) * freq) * amp;
+ }
+ return height - this.yOffset + y;
+ }
+
+ update() {
+ this.offset += this.speed;
+ }
+
+ draw() {
+ ctx.fillStyle = this.color;
+ ctx.beginPath();
+ ctx.moveTo(0, height);
+ for(let x=0; x<=width; x+=5) {
+ const y = this.getHeight(x);
+ ctx.lineTo(x, y);
+ }
+ ctx.lineTo(width, height);
+ ctx.closePath();
+ ctx.fill();
+ }
+}
+
+class Dragon {
+ constructor() {
+ this.x = width * 0.5;
+ this.y = height * 0.4;
+ this.angle = 0;
+ this.wingPhase = 0;
+ this.breathingFire = false;
+ this.fireTimer = 0;
+ this.targetX = width * 0.5;
+ this.targetY = height * 0.4;
+ }
+
+ update(time) {
+ const t = time * 0.0008;
+ this.targetX = width * 0.5 + Math.sin(t) * width * 0.25;
+ this.targetY = height * 0.35 + Math.sin(t * 1.3) * height * 0.12;
+
+ this.x += (this.targetX - this.x) * 0.02;
+ this.y += (this.targetY - this.y) * 0.02;
+
+ const dx = this.targetX - this.x;
+ const dy = this.targetY - this.y;
+ this.angle = Math.atan2(dy, dx) * 0.5;
+
+ this.wingPhase += 0.12;
+
+ if (this.breathingFire) {
+ this.fireTimer--;
+ if (this.fireTimer <= 0) this.breathingFire = false;
+
+ const mouthX = this.x + Math.cos(this.angle) * 60;
+ const mouthY = this.y + Math.sin(this.angle) * 10;
+ for(let i=0; i<4; i++) {
+ fireParticles.push(new FireParticle(mouthX, mouthY, this.angle, 1.5));
+ }
+ }
+ }
+
+ draw() {
+ ctx.save();
+ ctx.translate(this.x, this.y);
+ ctx.rotate(this.angle);
+
+ const wingY = Math.sin(this.wingPhase) * 30;
+ const wingScale = 0.8 + Math.sin(this.wingPhase) * 0.2;
+
+ ctx.fillStyle = '#080808';
+ if (this.breathingFire) {
+ ctx.shadowColor = '#ff4500';
+ ctx.shadowBlur = 20;
+ } else {
+ ctx.shadowBlur = 0;
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(-40, 0);
+ ctx.quadraticCurveTo(-60, wingY * wingScale, -80, -40 + wingY);
+ ctx.quadraticCurveTo(-50, -20 + wingY * 0.5, -20, -10);
+ ctx.quadraticCurveTo(-50, 20 - wingY * 0.5, -80, 40 + wingY);
+ ctx.quadraticCurveTo(-60, wingY * wingScale, -40, 5);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.ellipse(0, 0, 45, 18, 0, 0, Math.PI*2);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(30, -8);
+ ctx.lineTo(55, -5);
+ ctx.lineTo(60, 0);
+ ctx.lineTo(55, 5);
+ ctx.lineTo(30, 8);
+ if (this.breathingFire) {
+ ctx.lineTo(50, 0);
+ } else {
+ ctx.lineTo(45, 0);
+ }
+ ctx.closePath();
+ ctx.fill();
+
+ ctx.fillStyle = this.breathingFire ? '#ff6600' : '#330000';
+ ctx.beginPath();
+ ctx.arc(45, -3, 3, 0, Math.PI*2);
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.moveTo(-40, -5);
+ for(let i=0; i<5; i++) {
+ const sx = -30 - i*12;
+ const sy = -8 - Math.abs(Math.sin(i + this.wingPhase*0.5)) * 8;
+ ctx.lineTo(sx, sy);
+ ctx.lineTo(sx-6, -5);
+ }
+ ctx.lineTo(-90, 0);
+ ctx.strokeStyle = '#080808';
+ ctx.lineWidth = 3;
+ ctx.stroke();
+
+ ctx.restore();
+ ctx.shadowBlur = 0;
+ }
+
+ breatheFire() {
+ this.breathingFire = true;
+ this.fireTimer = 90;
+ }
+}
+
+const snow = Array(200).fill().map(() => new SnowParticle());
+const mountains = [
+ new MountainLayer('#0d1b2a', 0.1, 80, 150, [[0.003, 40], [0.01, 20], [0.03, 5]]),
+ new MountainLayer('#1b263b', 0.3, 60, 100, [[0.004, 30], [0.015, 15], [0.04, 4]]),
+ new MountainLayer('#415a77', 0.8, 40, 50, [[0.005, 25], [0.02, 10], [0.05, 3]])
+];
+const dragon = new Dragon();
+let fireParticles = [];
+
+canvas.addEventListener('mousedown', () => dragon.breatheFire());
+canvas.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ dragon.breatheFire();
+});
+
+function drawMoon() {
+ const mx = width * 0.85;
+ const my = height * 0.15;
+
+ const glow = ctx.createRadialGradient(mx, my, 0, mx, my, 100);
+ glow.addColorStop(0, 'rgba(200,220,255,0.3)');
+ glow.addColorStop(0.5, 'rgba(200,220,255,0.1)');
+ glow.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = glow;
+ ctx.fillRect(mx-100, my-100, 200, 200);
+
+ ctx.fillStyle = '#e0e0e0';
+ ctx.beginPath();
+ ctx.arc(mx, my, 30, 0, Math.PI*2);
+ ctx.fill();
+
+ ctx.fillStyle = '#c0c0c0';
+ ctx.beginPath();
+ ctx.arc(mx-8, my-5, 5, 0, Math.PI*2);
+ ctx.arc(mx+10, my+8, 7, 0, Math.PI*2);
+ ctx.arc(mx-5, my+12, 4, 0, Math.PI*2);
+ ctx.fill();
+}
+
+function animate(time) {
+ const grad = ctx.createLinearGradient(0, 0, 0, height);
+ grad.addColorStop(0, '#050510');
+ grad.addColorStop(0.4, '#1a1a3e');
+ grad.addColorStop(0.8, '#2d1b4e');
+ grad.addColorStop(1, '#0f3460');
+ ctx.fillStyle = grad;
+ ctx.fillRect(0, 0, width, height);
+
+ ctx.fillStyle = 'white';
+ for(let i=0; i<60; i++) {
+ const x = (i * 137.5) % width;
+ const y = (i * 71.3) % (height * 0.6);
+ const twinkle = Math.sin(time * 0.001 + i) * 0.5 + 0.5;
+ ctx.globalAlpha = 0.2 + twinkle * 0.6;
+ ctx.fillRect(x, y, (i%2)+1, (i%2)+1);
+ }
+ ctx.globalAlpha = 1;
+
+ drawMoon();
+
+ mountains.forEach(m => {
+ m.update();
+ m.draw();
+ });
+
+ dragon.update(time);
+
+ if (dragon.breathingFire) {
+ const mx = dragon.x + Math.cos(dragon.angle) * 60;
+ const my = dragon.y + Math.sin(dragon.angle) * 10;
+
+ ctx.save();
+ ctx.globalCompositeOperation = 'overlay';
+ const fireLight = ctx.createRadialGradient(mx, my, 0, mx, my, 400);
+ fireLight.addColorStop(0, 'rgba(255,100,50,0.6)');
+ fireLight.addColorStop(0.3, 'rgba(255,50,0,0.2)');
+ fireLight.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = fireLight;
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+ }
+
+ dragon.draw();
+
+ ctx.globalCompositeOperation = 'lighter';
+ for(let i=fireParticles.length-1; i>=0; i--) {
+ const p = fireParticles[i];
+ p.update();
+ p.draw();
+ if(p.life <= 0) fireParticles.splice(i, 1);
+ }
+ ctx.globalCompositeOperation = 'source-over';
+
+ snow.forEach(s => {
+ s.update();
+ s.draw();
+ });
+
+ requestAnimationFrame(animate);
+}
+
+animate(0);
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/02a362bd0b06/kimi.png b/data/artifacts/02a362bd0b06/kimi.png
new file mode 100644
index 0000000..7676bb0
Binary files /dev/null and b/data/artifacts/02a362bd0b06/kimi.png differ
diff --git a/data/artifacts/02a362bd0b06/qwen3-14b.html b/data/artifacts/02a362bd0b06/qwen3-14b.html
new file mode 100644
index 0000000..3bf0dfc
--- /dev/null
+++ b/data/artifacts/02a362bd0b06/qwen3-14b.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body {
+ margin: 0;
+ overflow: hidden;
+ background: linear-gradient(to bottom, #87CEEB, #FFFFFF);
+}
+
+#scene {
+ position: relative;
+ width: 100vw;
+ height: 100vh;
+ overflow: hidden;
+}
+
+.mountains {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: 20%;
+ background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M0 100 L100 100 L50 0 Z" fill="#228B22"/></svg>') repeat-x;
+}
+
+.snowflake {
+ position: absolute;
+ width: 10px;
+ height: 10px;
+ background: white;
+ border-radius: 50%;
+ animation: fall linear infinite;
+}
+
+@keyframes fall {
+ 0% { transform: translateY(-10px); opacity: 0; }
+ 50% { opacity: 1; }
+ 100% { transform: translateY(100vh); opacity: 0; }
+}
+
+.dragon {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M10 40 Q30 10 70 40 T130 40" fill="green"/><path d="M70 40 L90 20 L110 40" fill="red"/></svg>');
+ animation: fly 5s linear infinite;
+}
+
+@keyframes fly {
+ 0% { left: -100px; top: 40vh; }
+ 100% { left: 110%; top: 40vh; }
+}
+
+.fire {
+ position: absolute;
+ width: 20px;
+ height: 20px;
+ background: radial-gradient(circle, orange, yellow);
+ opacity: 0;
+ animation: fire 1s ease-out forwards;
+}
+
+@keyframes fire {
+ 0% { transform: scale(0.5); opacity: 1; }
+ 100% { transform: scale(3); opacity: 0; }
+}
+</style>
+</head>
+<body>
+<div id="scene">
+ <div class="mountains"></div>
+ <div class="dragon"></div>
+</div>
+<script>
+window.onload = function() {
+ const scene = document.getElementById('scene');
+ // Create snowflakes
+ for (let i = 0; i < 100; i++) {
+ const snowflake = document.createElement('div');
+ snowflake.classList.add('snowflake');
+ snowflake.style.left = Math.random() * 100 + 'vw';
+ snowflake.style.animationDuration = (1 + Math.random() * 3) + 's';
+ snowflake.style.animationDelay = Math.random() * 3 + 's';
+ scene.appendChild(snowflake);
+ }
+
+ // Handle fire breath on click
+ scene.addEventListener('click', function(event) {
+ const fire = document.createElement('div');
+ fire.className = 'fire';
+ const dragon = document.querySelector('.dragon');
+ const rect = dragon.getBoundingClientRect();
+ fire.style.left = (rect.left + rect.width/2 - 10) + 'px';
+ fire.style.top = (rect.top + rect.height/2 - 10) + 'px';
+ scene.appendChild(fire);
+ setTimeout(() => fire.remove(), 1000);
+ });
+};
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/02a362bd0b06/qwen3-14b.png b/data/artifacts/02a362bd0b06/qwen3-14b.png
new file mode 100644
index 0000000..a0a67a7
Binary files /dev/null and b/data/artifacts/02a362bd0b06/qwen3-14b.png differ
diff --git a/data/artifacts/0ac1af2eaed4/claude-code.html b/data/artifacts/0ac1af2eaed4/claude-code.html
new file mode 100644
index 0000000..1e02fa4
--- /dev/null
+++ b/data/artifacts/0ac1af2eaed4/claude-code.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">
+<title>Particle Fireworks</title>
+<style>
+ html, body { margin: 0; height: 100%; overflow: hidden; background: #05060f; }
+ canvas { display: block; cursor: crosshair; }
+ #hud { position: fixed; top: 12px; left: 12px; color: #9aa4c7;
+ font: 13px/1.6 system-ui, sans-serif; user-select: none; }
+ #hud button { background: #1b2140; color: #cdd6ff; border: 1px solid #39406b;
+ border-radius: 6px; padding: 4px 12px; font: inherit; cursor: pointer; }
+ #hud button:hover { background: #262e58; }
+</style>
+</head>
+<body>
+<canvas id="sky"></canvas>
+<div id="hud">
+ <button id="autoBtn">auto-show: on</button>
+ <span> click anywhere to launch · press A to toggle</span>
+</div>
+<script>
+'use strict';
+const canvas = document.getElementById('sky');
+const ctx = canvas.getContext('2d');
+let W, H;
+function resize() { W = canvas.width = innerWidth; H = canvas.height = innerHeight; }
+addEventListener('resize', resize);
+resize();
+
+const GRAVITY = 0.05, FRICTION = 0.985, TRAIL = 7;
+const rand = (a, b) => a + Math.random() * (b - a);
+const rockets = [], particles = [];
+
+class Rocket {
+ constructor(tx, ty) {
+ this.x = tx + rand(-25, 25);
+ this.y = H;
+ this.ty = Math.min(ty, H * 0.85);
+ // launch speed chosen so the rocket's apex lands at the target height
+ this.vy = -Math.sqrt(2 * GRAVITY * (H - this.ty)) - rand(0.2, 0.8);
+ this.vx = (tx - this.x) / (Math.abs(this.vy) / GRAVITY);
+ this.hue = rand(0, 360);
+ this.trail = [];
+ }
+ update() {
+ this.trail.push({ x: this.x, y: this.y });
+ if (this.trail.length > TRAIL) this.trail.shift();
+ this.x += this.vx;
+ this.y += this.vy;
+ this.vy += GRAVITY;
+ if (this.vy >= 0 || this.y <= this.ty) { explode(this.x, this.y, this.hue); return true; }
+ return false;
+ }
+ draw() {
+ const tail = this.trail[0] || this;
+ ctx.strokeStyle = `hsl(${this.hue} 100% 75%)`;
+ ctx.lineWidth = 2.5;
+ ctx.beginPath();
+ ctx.moveTo(tail.x, tail.y);
+ ctx.lineTo(this.x, this.y);
+ ctx.stroke();
+ }
+}
+
+class Particle {
+ constructor(x, y, hue) {
+ const angle = rand(0, Math.PI * 2);
+ // sqrt gives an even fill of the burst disc instead of a hollow ring
+ const speed = Math.sqrt(Math.random()) * rand(5, 9);
+ this.x = x; this.y = y;
+ this.vx = Math.cos(angle) * speed;
+ this.vy = Math.sin(angle) * speed;
+ this.hue = hue + rand(-18, 18);
+ this.life = 1;
+ this.decay = rand(0.008, 0.022);
+ this.trail = [];
+ }
+ update() {
+ this.trail.push({ x: this.x, y: this.y });
+ if (this.trail.length > TRAIL) this.trail.shift();
+ this.vx *= FRICTION;
+ this.vy = this.vy * FRICTION + GRAVITY;
+ this.x += this.vx;
+ this.y += this.vy;
+ this.life -= this.decay;
+ return this.life <= 0;
+ }
+ draw() {
+ if (this.life < 0.35 && Math.random() < 0.4) return; // twinkle as it dies
+ const tail = this.trail[0] || this;
+ ctx.strokeStyle = `hsla(${this.hue} 100% ${45 + this.life * 30}% / ${this.life})`;
+ ctx.lineWidth = 1 + this.life * 1.5;
+ ctx.beginPath();
+ ctx.moveTo(tail.x, tail.y);
+ ctx.lineTo(this.x, this.y);
+ ctx.stroke();
+ }
+}
+
+function explode(x, y, hue) {
+ const count = Math.floor(rand(70, 120));
+ const twoTone = Math.random() < 0.3 ? hue + 180 : hue;
+ for (let i = 0; i < count; i++) particles.push(new Particle(x, y, i % 2 ? hue : twoTone));
+ // brief flash at the burst point
+ const flash = ctx.createRadialGradient(x, y, 0, x, y, 60);
+ flash.addColorStop(0, `hsla(${hue} 100% 90% / 0.9)`);
+ flash.addColorStop(1, 'transparent');
+ ctx.fillStyle = flash;
+ ctx.fillRect(x - 60, y - 60, 120, 120);
+}
+
+function launch(tx, ty) { rockets.push(new Rocket(tx, ty)); }
+
+let autoOn = true, autoTimer = 0;
+function tick(now) {
+ // translucent fill (not clearRect) so everything leaves a fading trail
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.fillStyle = 'rgba(5, 6, 15, 0.22)';
+ ctx.fillRect(0, 0, W, H);
+ ctx.globalCompositeOperation = 'lighter'; // additive blend = glow where sparks overlap
+
+ for (let i = rockets.length - 1; i >= 0; i--) {
+ if (rockets[i].update()) rockets.splice(i, 1);
+ else rockets[i].draw();
+ }
+ for (let i = particles.length - 1; i >= 0; i--) {
+ if (particles[i].update()) particles.splice(i, 1);
+ else particles[i].draw();
+ }
+
+ if (autoOn && now > autoTimer) {
+ launch(rand(W * 0.1, W * 0.9), rand(H * 0.1, H * 0.45));
+ autoTimer = now + rand(500, 1600);
+ }
+ requestAnimationFrame(tick);
+}
+requestAnimationFrame(tick);
+
+canvas.addEventListener('pointerdown', e => {
+ launch(e.clientX, e.clientY);
+ autoTimer = performance.now() + 4000; // give the user the sky for a few seconds
+});
+
+const autoBtn = document.getElementById('autoBtn');
+function setAuto(on) {
+ autoOn = on;
+ autoBtn.textContent = `auto-show: ${on ? 'on' : 'off'}`;
+}
+autoBtn.addEventListener('click', e => { e.stopPropagation(); setAuto(!autoOn); });
+addEventListener('keydown', e => { if (e.key.toLowerCase() === 'a') setAuto(!autoOn); });
+</script>
+</body>
+</html>
diff --git a/data/artifacts/0ac1af2eaed4/claude-code.png b/data/artifacts/0ac1af2eaed4/claude-code.png
new file mode 100644
index 0000000..5abf302
Binary files /dev/null and b/data/artifacts/0ac1af2eaed4/claude-code.png differ
diff --git a/data/artifacts/0ac1af2eaed4/qwen25-7b.html b/data/artifacts/0ac1af2eaed4/qwen25-7b.html
new file mode 100644
index 0000000..4c249ee
--- /dev/null
+++ b/data/artifacts/0ac1af2eaed4/qwen25-7b.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <style>
+ body { margin: 0; overflow: hidden; background-color: #000; }
+ canvas { display: block; }
+ </style>
+</head>
+<body>
+<canvas id="fireworks"></canvas>
+<script>
+const c = document.getElementById("fireworks");
+c.width = c.height = innerWidth;
+const ctx = c.getContext("2d");
+
+let autoShow = false;
+
+function Rocket(x, y) {
+ this.x = x; this.y = y;
+ this.angle = Math.random() * 2 * Math.PI;
+ this.speed = (Math.random() + 0.5) * 3;
+}
+
+Rocket.prototype.update = function() {
+ if(this.y > c.height - 10) {
+ this.explode();
+ return;
+ }
+ this.angle -= 0.01;
+ this.x += Math.cos(this.angle) * (this.speed--);
+ this.y += Math.sin(this.angle) * (this.speed--);
+};
+
+Rocket.prototype.explode = function() {
+ for(let i = 0; i < 50; i++) {
+ const p = new Particle(this.x, this.y + 10);
+ particles.push(p);
+ }
+}
+
+function Particle(x, y) {
+ this.x = x;
+ this.y = y;
+ this.size = Math.random() * 2 + 1;
+ this.speedY = -Math.random() * 5;
+ this.color = `hsl(${Math.random() * 360}, 100%, 70%)`;
+}
+
+Particle.prototype.update = function() {
+ this.y += this.speedY;
+ if(this.size > 0) {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI*2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ }
+};
+
+const rockets = [];
+const particles = [];
+
+function launchRocket(e) {
+ const x = e.offsetX || e.layerX;
+ const y = e.offsetY || e.layerY;
+ rockets.push(new Rocket(x, y));
+}
+
+c.addEventListener("click", launchRocket);
+
+function autoLaunch() {
+ if(!autoShow) return;
+ rockets.push(new Rocket(c.width/2, c.height - 10));
+ requestAnimationFrame(autoLaunch);
+}
+
+setInterval(() => {
+ if(Math.random() > 0.975) autoShow = !autoShow;
+}, 3000);
+
+function drawParticles() {
+ ctx.globalAlpha = 0.8;
+ particles.forEach(p => p.update());
+ ctx.globalAlpha = 1;
+}
+
+function drawRockets() {
+ rockets.forEach(r => r.update());
+ if(autoShow) autoLaunch();
+}
+
+setInterval(drawParticles, 25);
+setInterval(drawRockets, 30);
+
+window.requestAnimationFrame(() => {});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/0ac1af2eaed4/qwen25-7b.png b/data/artifacts/0ac1af2eaed4/qwen25-7b.png
new file mode 100644
index 0000000..ad57eb4
Binary files /dev/null and b/data/artifacts/0ac1af2eaed4/qwen25-7b.png differ
diff --git a/data/artifacts/34ecbeae4e1f/gemma3-12b.html b/data/artifacts/34ecbeae4e1f/gemma3-12b.html
new file mode 100644
index 0000000..d7e99ae
--- /dev/null
+++ b/data/artifacts/34ecbeae4e1f/gemma3-12b.html
@@ -0,0 +1,358 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Wallcovering SKUs</title>
+ <style>
+ :root {
+ --bg: #f6f4f0;
+ --card: #ffffff;
+ --ink: #23211d;
+ --muted: #8a8579;
+ --accent: #7a6a4f;
+ --line: #e8e4dc;
+ --hover: #faf8f4;
+ }
+
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ body {
+ font-family: "Avenir Next", "Segoe UI", -apple-system, Helvetica, Arial, sans-serif;
+ background: var(--bg);
+ color: var(--ink);
+ min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ padding: 48px 20px;
+ }
+
+ .wrap {
+ width: 100%;
+ max-width: 880px;
+ }
+
+ header {
+ margin-bottom: 28px;
+ }
+
+ h1 {
+ font-weight: 500;
+ font-size: 26px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ }
+
+ .sub {
+ color: var(--muted);
+ font-size: 13px;
+ letter-spacing: 0.05em;
+ margin-top: 6px;
+ }
+
+ .toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ margin-bottom: 16px;
+ flex-wrap: wrap;
+ }
+
+ .search-box {
+ position: relative;
+ flex: 1;
+ min-width: 220px;
+ max-width: 360px;
+ }
+
+ .search-box svg {
+ position: absolute;
+ left: 12px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 15px;
+ height: 15px;
+ stroke: var(--muted);
+ fill: none;
+ stroke-width: 2;
+ pointer-events: none;
+ }
+
+ #search {
+ width: 100%;
+ padding: 10px 14px 10px 36px;
+ font-size: 14px;
+ font-family: inherit;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: var(--card);
+ color: var(--ink);
+ outline: none;
+ transition: border-color .15s, box-shadow .15s;
+ }
+
+ #search:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px rgba(122, 106, 79, .12);
+ }
+
+ #count {
+ color: var(--muted);
+ font-size: 13px;
+ letter-spacing: 0.04em;
+ }
+
+ .card {
+ background: var(--card);
+ border: 1px solid var(--line);
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 1px 3px rgba(35, 33, 29, .05);
+ }
+
+ table {
+ width: 100%;
+ border-collapse: collapse;
+ }
+
+ thead th {
+ text-align: left;
+ font-size: 11px;
+ font-weight: 600;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--muted);
+ padding: 14px 18px;
+ border-bottom: 1px solid var(--line);
+ cursor: pointer;
+ user-select: none;
+ white-space: nowrap;
+ transition: color .15s;
+ }
+
+ thead th:hover {
+ color: var(--accent);
+ }
+
+ thead th .arrow {
+ display: inline-block;
+ width: 1em;
+ color: var(--accent);
+ font-size: 10px;
+ }
+
+ tbody td {
+ padding: 14px 18px;
+ font-size: 14px;
+ border-bottom: 1px solid var(--line);
+ }
+
+ tbody tr:last-child td {
+ border-bottom: none;
+ }
+
+ tbody tr {
+ transition: background .12s;
+ }
+
+ tbody tr:hover {
+ background: var(--hover);
+ }
+
+ td.sku {
+ font-family: "SF Mono", Menlo, Consolas, monospace;
+ font-size: 13px;
+ color: var(--accent);
+ }
+
+ td.num {
+ text-align: right;
+ font-variant-numeric: tabular-nums;
+ }
+
+ th.num {
+ text-align: right;
+ }
+
+ .stock-pill {
+ display: inline-block;
+ padding: 3px 10px;
+ border-radius: 999px;
+ font-size: 12px;
+ font-variant-numeric: tabular-nums;
+ }
+
+ .stock-hi {
+ background: #e8efe4;
+ color: #45663a;
+ }
+
+ .stock-mid {
+ background: #f4eedd;
+ color: #7d6a33;
+ }
+
+ .stock-lo {
+ background: #f6e5e0;
+ color: #8c4a3b;
+ }
+
+ .empty {
+ padding: 40px 18px;
+ text-align: center;
+ color: var(--muted);
+ font-size: 14px;
+ display: none;
+ }
+ </style>
+</head>
+
+<body>
+ <div class="wrap">
+ <header>
+ <h1>Wallcovering Library</h1>
+ <div class="sub">Sample SKU catalog — click a column to sort, type to filter</div>
+ </header>
+
+ <div class="toolbar">
+ <div class="search-box">
+ <svg viewBox="0 0 24 24">
+ <circle cx="11" cy="11" r="7" />
+ <line x1="16.5" y1="16.5" x2="21" y2="21" />
+ </svg>
+ <input id="search" type="text" placeholder="Search sku, pattern, colorway…" autocomplete="off">
+ </div>
+ <div id="count"></div >
+ </div>
+
+ <div class="card">
+ <table id="tbl">
+ <thead>
+ <tr>
+ <th data-key="sku">SKU<span class="arrow"></span></th>
+ <th data-key="pattern">Pattern<span class="arrow"></span></th>
+ <th data-key="colorway">Colorway<span class="arrow"></span></th>
+ <th data-key="price" class="num">Price<span class="arrow"></span></th>
+ <th data-key="stock" class="num">Stock<span class="arrow"></span></th>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+ <div class="empty" id="empty">No matching SKUs.</div>
+ </div>
+ </div>
+
+ <script>
+ (function () {
+ var data = [
+ { sku: "DW-1042", pattern: "Meadow Damask", colorway: "Sage", price: 128.00, stock: 42 },
+ { sku: "DW-2310", pattern: "Coastal Grass", colorway: "Oatmeal", price: 96.50, stock: 8 },
+ { sku: "DW-3177", pattern: "Chinoiserie Vine", colorway: "Celadon", price: 245.00, stock: 15 },
+ { sku: "DW-4508", pattern: "Herringbone Silk", colorway: "Champagne", price: 189.75, stock: 27 },
+ { sku: "DW-5624", pattern: "Deco Fan", colorway: "Midnight", price: 142.00, stock: 3 },
+ { sku: "DW-6081", pattern: "Linen Weave", colorway: "Alabaster", price: 74.25, stock: 63 }
+ ];
+
+ var sortKey = null;
+ var sortDir = 1;
+ var tbody = document.querySelector("#tbl tbody");
+ var search = document.getElementById("search");
+ var countEl = document.getElementById("count");
+ var emptyEl = document.getElementById("empty");
+ var headers = document.querySelectorAll("#tbl thead th");
+
+ function stockClass(n) {
+ if (n <= 5) return "stock-lo";
+ if (n <= 20) return "stock-mid";
+ return "stock-hi";
+ }
+
+ function fmtPrice(p) {
+ return "$" + p.toFixed(2);
+ }
+
+ function render() {
+ var q = search.value.trim().toLowerCase();
+ var rows = data.filter(function (r) {
+ if (!q) return true;
+ return (r.sku + " " + r.pattern + " " + r.colorway + " " + r.price + " " + r.stock)
+ .toLowerCase().indexOf(q) !== -1;
+ });
+
+ if (sortKey) {
+ rows = rows.slice().sort(function (a, b) {
+ var av = a[sortKey], bv = b[sortKey];
+ if (typeof av === "string") {
+ return av.localeCompare(bv) * sortDir;
+ }
+ return (av - bv) * sortDir;
+ });
+ }
+
+ tbody.innerHTML = "";
+ rows.forEach(function (r) {
+ var tr = document.createElement("tr");
+
+ var tds = [
+ { cls: "sku", text: r.sku },
+ { cls: "", text: r.pattern },
+ { cls: "", text: r.colorway },
+ { cls: "num", text: fmtPrice(r.price) }
+ ];
+ tds.forEach(function (c) {
+ var td = document.createElement("td");
+ if (c.cls) td.className = c.cls;
+ td.textContent = c.text;
+ tr.appendChild(td);
+ });
+
+ var tdStock = document.createElement("td");
+ tdStock.className = "num";
+ var pill = document.createElement("span");
+ pill.className = "stock-pill " + stockClass(r.stock);
+ pill.textContent = r.stock + " rolls";
+ tdStock.appendChild(pill);
+ tr.appendChild(tdStock);
+
+ tbody.appendChild(tr);
+ });
+
+ emptyEl.style.display = rows.length ? "none" : "block";
+ countEl.textContent = rows.length + " of " + data.length + " SKUs";
+
+ headers.forEach(function (th) {
+ var arrow = th.querySelector(".arrow");
+ if (th.dataset.key === sortKey) {
+ arrow.textContent = sortDir === 1 ? "\u25B2" : "\u25BC";
+ } else {
+ arrow.textContent = "";
+ }
+ });
+ }
+
+ headers.forEach(function (th) {
+ th.addEventListener("click", function () {
+ var key = th.dataset.key;
+ if (sortKey === key) {
+ sortDir = -sortDir;
+ } else {
+ sortKey = key;
+ sortDir = 1;
+ }
+ render();
+ });
+ });
+
+ search.addEventListener("input", render);
+ render();
+ })();
+ </script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/data/artifacts/34ecbeae4e1f/gemma3-12b.png b/data/artifacts/34ecbeae4e1f/gemma3-12b.png
new file mode 100644
index 0000000..10fab4b
Binary files /dev/null and b/data/artifacts/34ecbeae4e1f/gemma3-12b.png differ
diff --git a/data/artifacts/34ecbeae4e1f/hermes3-8b.html b/data/artifacts/34ecbeae4e1f/hermes3-8b.html
new file mode 100644
index 0000000..f08cb3f
--- /dev/null
+++ b/data/artifacts/34ecbeae4e1f/hermes3-8b.html
@@ -0,0 +1,268 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Wallcovering SKUs</title>
+<style>
+ :root {
+ --bg: #f6f4f0;
+ --card: #ffffff;
+ --ink: #23211d;
+ --muted: #8a8579;
+ --accent: #7a6a4f;
+ --line: #e8e4dc;
+ --hover: #faf8f4;
+ }
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+ body {
+ font-family: "Avenir Next", "Segoe UI", -apple-system, Helvetica, Arial, sans-serif;
+ background: var(--bg);
+ color: var(--ink);
+ min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ padding: 48px 20px;
+ }
+ .wrap { width: 100%; max-width: 880px; }
+ header { margin-bottom: 28px; }
+ h1 {
+ font-weight: 500;
+ font-size: 26px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ }
+ .sub {
+ color: var(--muted);
+ font-size: 13px;
+ letter-spacing: 0.05em;
+ margin-top: 6px;
+ }
+ .toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ margin-bottom: 16px;
+ flex-wrap: wrap;
+ }
+ .search-box { position: relative; flex: 1; min-width: 220px; max-width: 360px; }
+ .search-box svg {
+ position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
+ width: 15px; height: 15px; stroke: var(--muted); fill: none; stroke-width: 2;
+ pointer-events: none;
+ }
+ #search {
+ width: 100%;
+ padding: 10px 14px 10px 36px;
+ font-size: 14px;
+ font-family: inherit;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: var(--card);
+ color: var(--ink);
+ outline: none;
+ transition: border-color .15s, box-shadow .15s;
+ }
+ #search:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px rgba(122,106,79,.12);
+ }
+ #count { color: var(--muted); font-size: 13px; letter-spacing: 0.04em; }
+ .card {
+ background: var(--card);
+ border: 1px solid var(--line);
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 1px 3px rgba(35,33,29,.05);
+ }
+ table { width: 100%; border-collapse: collapse; }
+ thead th {
+ text-align: left;
+ font-size: 11px;
+ font-weight: 600;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--muted);
+ padding: 14px 18px;
+ border-bottom: 1px solid var(--line);
+ cursor: pointer;
+ user-select: none;
+ white-space: nowrap;
+ transition: color .15s;
+ }
+ thead th:hover { color: var(--accent); }
+ thead th .arrow {
+ display: inline-block;
+ width: 1em;
+ color: var(--accent);
+ font-size: 10px;
+ }
+ tbody td {
+ padding: 14px 18px;
+ font-size: 14px;
+ border-bottom: 1px solid var(--line);
+ }
+ tbody tr:last-child td { border-bottom: none; }
+ tbody tr { transition: background .12s; }
+ tbody tr:hover { background: var(--hover); }
+ td.sku { font-family: "SF Mono", Menlo, Consolas, monospace; font-size: 13px; color: var(--accent); }
+ td.num { text-align: right; font-variant-numeric: tabular-nums; }
+ th.num { text-align: right; }
+ .stock-pill {
+ display: inline-block;
+ padding: 3px 10px;
+ border-radius: 999px;
+ font-size: 12px;
+ font-variant-numeric: tabular-nums;
+ }
+ .stock-hi { background: #e8efe4; color: #45663a; }
+ .stock-mid { background: #f4eedd; color: #7d6a33; }
+ .stock-lo { background: #f6e5e0; color: #8c4a3b; }
+ .empty {
+ padding: 40px 18px;
+ text-align: center;
+ color: var(--muted);
+ font-size: 14px;
+ display: none;
+ }
+</style>
+</head>
+<body>
+<div class="wrap">
+ <header>
+ <h1>Wallcovering Library</h1>
+ <div class="sub">Sample SKU catalog — click a column to sort, type to filter</div>
+ </header>
+
+ <div class="toolbar">
+ <div class="search-box">
+ <svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><line x1="16.5" y1="16.5" x2="21" y2="21"/></svg>
+ <input id="search" type="text" placeholder="Search sku, pattern, colorway…" autocomplete="off">
+ </div>
+ <div id="count"></div>
+ </div>
+
+ <div class="card">
+ <table id="tbl">
+ <thead>
+ <tr>
+ <th data-key="sku">SKU<span class="arrow"></span></th>
+ <th data-key="pattern">Pattern<span class="arrow"></span></th>
+ <th data-key="colorway">Colorway<span class="arrow"></span></th>
+ <th data-key="price" class="num">Price<span class="arrow"></span></th>
+ <th data-key="stock" class="num">Stock<span class="arrow"></span></th>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+ <div class="empty" id="empty">No matching SKUs.</div>
+ </div>
+</div>
+
+<script>
+(function () {
+ var data = [
+ { sku: "DW-1042", pattern: "Meadow Damask", colorway: "Sage", price: 128.00, stock: 42 },
+ { sku: "DW-2310", pattern: "Coastal Grass", colorway: "Oatmeal", price: 96.50, stock: 8 },
+ { sku: "DW-3177", pattern: "Chinoiserie Vine", colorway: "Celadon", price: 245.00, stock: 15 },
+ { sku: "DW-4508", pattern: "Herringbone Silk", colorway: "Champagne", price: 189.75, stock: 27 },
+ { sku: "DW-5624", pattern: "Deco Fan", colorway: "Midnight", price: 142.00, stock: 3 },
+ { sku: "DW-6081", pattern: "Linen Weave", colorway: "Alabaster", price: 74.25, stock: 63 }
+ ];
+
+ var sortKey = null, sortDir = 1;
+ var tbody = document.querySelector("#tbl tbody");
+ var search = document.getElementById("search");
+ var countEl = document.getElementById("count");
+ var emptyEl = document.getElementById("empty");
+ var headers = document.querySelectorAll("#tbl thead th");
+
+ function stockClass(n) {
+ if (n <= 5) return "stock-lo";
+ if (n <= 20) return "stock-mid";
+ return "stock-hi";
+ }
+
+ function fmtPrice(p) {
+ return "$" + p.toFixed(2);
+ }
+
+ function render() {
+ var q = search.value.trim().toLowerCase();
+ var rows = data.filter(function (r) {
+ if (!q) return true;
+ return (r.sku + " " + r.pattern + " " + r.colorway + " " + r.price + " " + r.stock)
+ .toLowerCase().indexOf(q) !== -1;
+ });
+
+ if (sortKey) {
+ rows = rows.slice().sort(function (a, b) {
+ var av = a[sortKey], bv = b[sortKey];
+ if (typeof av === "string") {
+ return av.localeCompare(bv) * sortDir;
+ }
+ return (av - bv) * sortDir;
+ });
+ }
+
+ tbody.innerHTML = "";
+ rows.forEach(function (r) {
+ var tr = document.createElement("tr");
+
+ var tds = [
+ { cls: "sku", text: r.sku },
+ { cls: "", text: r.pattern },
+ { cls: "", text: r.colorway },
+ { cls: "num", text: fmtPrice(r.price) }
+ ];
+ tds.forEach(function (c) {
+ var td = document.createElement("td");
+ if (c.cls) td.className = c.cls;
+ td.textContent = c.text;
+ tr.appendChild(td);
+ });
+
+ var tdStock = document.createElement("td");
+ tdStock.className = "num";
+ var pill = document.createElement("span");
+ pill.className = "stock-pill " + stockClass(r.stock);
+ pill.textContent = r.stock + " rolls";
+ tdStock.appendChild(pill);
+ tr.appendChild(tdStock);
+
+ tbody.appendChild(tr);
+ });
+
+ emptyEl.style.display = rows.length ? "none" : "block";
+ countEl.textContent = rows.length + " of " + data.length + " SKUs";
+
+ headers.forEach(function (th) {
+ var arrow = th.querySelector(".arrow");
+ if (th.dataset.key === sortKey) {
+ arrow.textContent = sortDir === 1 ? "\u25B2" : "\u25BC";
+ } else {
+ arrow.textContent = "";
+ }
+ });
+ }
+
+ headers.forEach(function (th) {
+ th.addEventListener("click", function () {
+ var key = th.dataset.key;
+ if (sortKey === key) {
+ sortDir = -sortDir;
+ } else {
+ sortKey = key;
+ sortDir = 1;
+ }
+ render();
+ });
+ });
+
+ search.addEventListener("input", render);
+ render();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/34ecbeae4e1f/hermes3-8b.png b/data/artifacts/34ecbeae4e1f/hermes3-8b.png
new file mode 100644
index 0000000..10fab4b
Binary files /dev/null and b/data/artifacts/34ecbeae4e1f/hermes3-8b.png differ
diff --git a/data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.html b/data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..fb8cde4
--- /dev/null
+++ b/data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.html
@@ -0,0 +1,272 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Wallcovering SKUs</title>
+<style>
+ :root {
+ --bg: #f6f4f0;
+ --card: #ffffff;
+ --ink: #23211d;
+ --muted: #8a8579;
+ --accent: #7a6a4f;
+ --line: #e8e4dc;
+ --hover: #faf8f4;
+ }
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+ body {
+ font-family: "Avenir Next", "Segoe UI", -apple-system, Helvetica, Arial, sans-serif;
+ background: var(--bg);
+ color: var(--ink);
+ min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ padding: 48px 20px;
+ }
+ .wrap { width: 100%; max-width: 880px; }
+ header { margin-bottom: 36px; text-align: center; }
+ h1 {
+ font-weight: 500;
+ font-size: 32px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--accent);
+ }
+ .sub {
+ color: var(--muted);
+ font-size: 16px;
+ letter-spacing: 0.05em;
+ margin-top: 8px;
+ }
+ .toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 24px;
+ margin-bottom: 24px;
+ flex-wrap: wrap;
+ }
+ .search-box { position: relative; flex: 1; min-width: 300px; max-width: 500px; }
+ .search-box svg {
+ position: absolute; left: 16px; top: 50%; transform: translateY(-50%);
+ width: 20px; height: 20px; stroke: var(--muted); fill: none; stroke-width: 2;
+ pointer-events: none;
+ }
+ #search {
+ width: 100%;
+ padding: 14px 20px 14px 56px;
+ font-size: 16px;
+ font-family: inherit;
+ border: 1px solid var(--line);
+ border-radius: 24px;
+ background: var(--card);
+ color: var(--ink);
+ outline: none;
+ transition: border-color .2s, box-shadow .2s;
+ }
+ #search:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 4px rgba(122,106,79,.15);
+ }
+ #count { color: var(--muted); font-size: 16px; letter-spacing: 0.04em; }
+ .card {
+ background: var(--card);
+ border: 1px solid var(--line);
+ border-radius: 24px;
+ overflow: hidden;
+ box-shadow: 0 4px 8px rgba(35,33,29,.07);
+ }
+ table { width: 100%; border-collapse: collapse; }
+ thead th {
+ text-align: left;
+ font-size: 14px;
+ font-weight: 600;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--muted);
+ padding: 20px 32px;
+ border-bottom: 1px solid var(--line);
+ cursor: pointer;
+ user-select: none;
+ white-space: nowrap;
+ transition: color .2s;
+ }
+ thead th:hover { color: var(--accent); }
+ thead th .arrow {
+ display: inline-block;
+ width: 1em;
+ margin-left: 4px;
+ color: var(--accent);
+ font-size: 12px;
+ vertical-align: middle;
+ }
+ tbody td {
+ padding: 20px 32px;
+ font-size: 16px;
+ border-bottom: 1px solid var(--line);
+ }
+ tbody tr:last-child td { border-bottom: none; }
+ tbody tr { transition: background .15s; }
+ tbody tr:hover { background: var(--hover); }
+ td.sku { font-family: "SF Mono", Menlo, Consolas, monospace; font-size: 14px; color: var(--accent); }
+ td.num { text-align: right; font-variant-numeric: tabular-nums; }
+ th.num { text-align: right; }
+ .stock-pill {
+ display: inline-block;
+ padding: 6px 12px;
+ border-radius: 999px;
+ font-size: 14px;
+ font-variant-numeric: tabular-nums;
+ vertical-align: middle;
+ }
+ .stock-hi { background: #e8efe4; color: #45663a; }
+ .stock-mid { background: #f4eedd; color: #7d6a33; }
+ .stock-lo { background: #f6e5e0; color: #8c4a3b; }
+ .empty {
+ padding: 60px 32px;
+ text-align: center;
+ color: var(--muted);
+ font-size: 18px;
+ display: none;
+ }
+</style>
+</head>
+<body>
+<div class="wrap">
+ <header>
+ <h1>Wallcovering Library</h1>
+ <div class="sub">Sample SKU catalog — click a column to sort, type to filter</div>
+ </header>
+
+ <div class="toolbar">
+ <div class="search-box">
+ <svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><line x1="16.5" y1="16.5" x2="21" y2="21"/></svg>
+ <input id="search" type="text" placeholder="Search sku, pattern, colorway…" autocomplete="off">
+ </div>
+ <div id="count"></div>
+ </div>
+
+ <div class="card">
+ <table id="tbl">
+ <thead>
+ <tr>
+ <th data-key="sku">SKU<span class="arrow"></span></th>
+ <th data-key="pattern">Pattern<span class="arrow"></span></th>
+ <th data-key="colorway">Colorway<span class="arrow"></span></th>
+ <th data-key="price" class="num">Price<span class="arrow"></span></th>
+ <th data-key="stock" class="num">Stock<span class="arrow"></span></th>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+ <div class="empty" id="empty">No matching SKUs.</div>
+ </div>
+</div>
+
+<script>
+(function () {
+ var data = [
+ { sku: "DW-1042", pattern: "Meadow Damask", colorway: "Sage", price: 128.00, stock: 42 },
+ { sku: "DW-2310", pattern: "Coastal Grass", colorway: "Oatmeal", price: 96.50, stock: 8 },
+ { sku: "DW-3177", pattern: "Chinoiserie Vine", colorway: "Celadon", price: 245.00, stock: 15 },
+ { sku: "DW-4508", pattern: "Herringbone Silk", colorway: "Champagne", price: 189.75, stock: 27 },
+ { sku: "DW-5624", pattern: "Deco Fan", colorway: "Midnight", price: 142.00, stock: 3 },
+ { sku: "DW-6081", pattern: "Linen Weave", colorway: "Alabaster", price: 74.25, stock: 63 }
+ ];
+
+ var sortKey = null, sortDir = 1;
+ var tbody = document.querySelector("#tbl tbody");
+ var search = document.getElementById("search");
+ var countEl = document.getElementById("count");
+ var emptyEl = document.getElementById("empty");
+ var headers = document.querySelectorAll("#tbl thead th");
+
+ function stockClass(n) {
+ if (n <= 5) return "stock-lo";
+ if (n <= 20) return "stock-mid";
+ return "stock-hi";
+ }
+
+ function fmtPrice(p) {
+ return "$" + p.toFixed(2);
+ }
+
+ function render() {
+ var q = search.value.trim().toLowerCase();
+ var rows = data.filter(function (r) {
+ if (!q) return true;
+ return (r.sku + " " + r.pattern + " " + r.colorway + " " + r.price + " " + r.stock)
+ .toLowerCase().indexOf(q) !== -1;
+ });
+
+ if (sortKey) {
+ rows = rows.slice().sort(function (a, b) {
+ var av = a[sortKey], bv = b[sortKey];
+ if (typeof av === "string") {
+ return av.localeCompare(bv) * sortDir;
+ }
+ return (av - bv) * sortDir;
+ });
+ }
+
+ tbody.innerHTML = "";
+ rows.forEach(function (r) {
+ var tr = document.createElement("tr");
+
+ var tds = [
+ { cls: "sku", text: r.sku },
+ { cls: "", text: r.pattern },
+ { cls: "", text: r.colorway },
+ { cls: "num", text: fmtPrice(r.price) }
+ ];
+ tds.forEach(function (c) {
+ var td = document.createElement("td");
+ if (c.cls) td.className = c.cls;
+ td.textContent = c.text;
+ tr.appendChild(td);
+ });
+
+ var tdStock = document.createElement("td");
+ tdStock.className = "num";
+ var pill = document.createElement("span");
+ pill.className = "stock-pill " + stockClass(r.stock);
+ pill.textContent = r.stock + " rolls";
+ tdStock.appendChild(pill);
+ tr.appendChild(tdStock);
+
+ tbody.appendChild(tr);
+ });
+
+ emptyEl.style.display = rows.length ? "none" : "block";
+ countEl.textContent = rows.length + " of " + data.length + " SKUs";
+
+ headers.forEach(function (th) {
+ var arrow = th.querySelector(".arrow");
+ if (th.dataset.key === sortKey) {
+ arrow.textContent = sortDir === 1 ? "\u25B2" : "\u25BC";
+ } else {
+ arrow.textContent = "";
+ }
+ });
+ }
+
+ headers.forEach(function (th) {
+ th.addEventListener("click", function () {
+ var key = th.dataset.key;
+ if (sortKey === key) {
+ sortDir = -sortDir;
+ } else {
+ sortKey = key;
+ sortDir = 1;
+ }
+ render();
+ });
+ });
+
+ search.addEventListener("input", render);
+ render();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.png b/data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..982d08a
Binary files /dev/null and b/data/artifacts/34ecbeae4e1f/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/34ecbeae4e1f/qwen3-14b.html b/data/artifacts/34ecbeae4e1f/qwen3-14b.html
new file mode 100644
index 0000000..67b0253
--- /dev/null
+++ b/data/artifacts/34ecbeae4e1f/qwen3-14b.html
@@ -0,0 +1,380 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Wallcovering SKUs</title>
+<style>
+:root {
+ --bg: #f6f4f0;
+ --card: #ffffff;
+ --ink: #23211d;
+ --muted: #8a8579;
+ --accent: #7a6a4f;
+ --line: #e8e4dc;
+ --hover: #faf8f4;
+ --focus: #f1f0ec;
+ --stock-high: #e8efe4;
+ --stock-mid: #f4eedd;
+ --stock-low: #f6e5e0;
+ --shadow: 0 2px 8px rgba(35,33,29,0.05);
+ --transition: 0.2s ease;
+}
+
+* { margin: 0; padding: 0; box-sizing: border-box; }
+body {
+ font-family: "Avenir Next", "Segoe UI", -apple-system, Helvetica, Arial, sans-serif;
+ background: var(--bg);
+ color: var(--ink);
+ min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ padding: 48px 20px;
+ transition: background var(--transition);
+}
+
+.wrap {
+ width: 100%;
+ max-width: 880px;
+ background: var(--card);
+ border-radius: 16px;
+ box-shadow: var(--shadow);
+ overflow: hidden;
+ transition: box-shadow var(--transition);
+}
+
+header {
+ padding: 24px 32px;
+ border-bottom: 1px solid var(--line);
+}
+
+h1 {
+ font-weight: 500;
+ font-size: 26px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ transition: color var(--transition);
+}
+
+h1:hover {
+ color: var(--accent);
+}
+
+.sub {
+ color: var(--muted);
+ font-size: 13px;
+ letter-spacing: 0.05em;
+ margin-top: 6px;
+}
+
+.toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ padding: 0 32px 16px;
+ flex-wrap: wrap;
+}
+
+.search-box {
+ position: relative;
+ flex: 1;
+ min-width: 220px;
+ max-width: 360px;
+ transition: box-shadow var(--transition);
+}
+
+.search-box svg {
+ position: absolute;
+ left: 12px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 18px;
+ height: 18px;
+ stroke: var(--muted);
+ fill: none;
+ stroke-width: 2;
+ pointer-events: none;
+ transition: stroke var(--transition);
+}
+
+#search {
+ width: 100%;
+ padding: 10px 14px 10px 36px;
+ font-size: 14px;
+ font-family: inherit;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: var(--card);
+ color: var(--ink);
+ outline: none;
+ transition: border-color var(--transition), box-shadow var(--transition);
+}
+
+#search:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px rgba(122,106,79,0.12);
+}
+
+#count {
+ color: var(--muted);
+ font-size: 13px;
+ letter-spacing: 0.04em;
+ min-width: 60px;
+ text-align: right;
+}
+
+.card {
+ overflow: hidden;
+ transition: box-shadow var(--transition);
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+ background: var(--card);
+ box-shadow: var(--shadow);
+}
+
+thead th {
+ text-align: left;
+ font-size: 11px;
+ font-weight: 600;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--muted);
+ padding: 14px 18px;
+ border-bottom: 1px solid var(--line);
+ cursor: pointer;
+ user-select: none;
+ white-space: nowrap;
+ position: relative;
+ transition: color var(--transition), background var(--transition);
+}
+
+thead th:hover {
+ background: var(--hover);
+ color: var(--accent);
+}
+
+thead th .arrow {
+ display: inline-block;
+ width: 1em;
+ color: var(--accent);
+ font-size: 10px;
+ margin-left: 6px;
+ transition: transform var(--transition);
+}
+
+tbody td {
+ padding: 14px 18px;
+ font-size: 14px;
+ border-bottom: 1px solid var(--line);
+ transition: background var(--transition);
+}
+
+tbody tr:last-child td {
+ border-bottom: none;
+}
+
+tbody tr:hover {
+ background: var(--hover);
+}
+
+td.sku {
+ font-family: "SF Mono", Menlo, Consolas, monospace;
+ font-size: 13px;
+ color: var(--accent);
+}
+
+td.num {
+ text-align: right;
+ font-variant-numeric: tabular-nums;
+}
+
+th.num {
+ text-align: right;
+}
+
+.stock-pill {
+ display: inline-block;
+ padding: 3px 10px;
+ border-radius: 999px;
+ font-size: 12px;
+ font-variant-numeric: tabular-nums;
+ transition: background var(--transition), color var(--transition);
+}
+
+.stock-hi {
+ background: var(--stock-high);
+ color: #45663a;
+ border: 1px solid #c6d9c3;
+}
+
+.stock-mid {
+ background: var(--stock-mid);
+ color: #7d6a33;
+ border: 1px solid #f0d4b5;
+}
+
+.stock-lo {
+ background: var(--stock-low);
+ color: #8c4a3b;
+ border: 1px solid #f2d0cc;
+}
+
+.empty {
+ padding: 40px 18px;
+ text-align: center;
+ color: var(--muted);
+ font-size: 14px;
+ display: none;
+ transition: opacity var(--transition);
+}
+
+.empty.show {
+ display: block;
+ opacity: 1;
+}
+</style>
+</head>
+<body>
+<div class="wrap">
+ <header>
+ <h1>Wallcovering Library</h1>
+ <div class="sub">Sample SKU catalog — click a column to sort, type to filter</div>
+ </header>
+
+ <div class="toolbar">
+ <div class="search-box">
+ <svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><line x1="16.5" y1="16.5" x2="21" y2="21"/></svg>
+ <input id="search" type="text" placeholder="Search sku, pattern, colorway…" autocomplete="off">
+ </div>
+ <div id="count"></div>
+ </div>
+
+ <div class="card">
+ <table id="tbl">
+ <thead>
+ <tr>
+ <th data-key="sku">SKU<span class="arrow">↑</span></th>
+ <th data-key="pattern">Pattern<span class="arrow">↑</span></th>
+ <th data-key="colorway">Colorway<span class="arrow">↑</span></th>
+ <th data-key="price" class="num">Price<span class="arrow">↑</span></th>
+ <th data-key="stock" class="num">Stock<span class="arrow">↑</span></th>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+ <div class="empty" id="empty">No matching SKUs.</div>
+ </div>
+</div>
+
+<script>
+(function () {
+ const data = [
+ { sku: "DW-1042", pattern: "Meadow Damask", colorway: "Sage", price: 128.00, stock: 42 },
+ { sku: "DW-2310", pattern: "Coastal Grass", colorway: "Oatmeal", price: 96.50, stock: 8 },
+ { sku: "DW-3177", pattern: "Chinoiserie Vine", colorway: "Celadon", price: 245.00, stock: 15 },
+ { sku: "DW-4508", pattern: "Herringbone Silk", colorway: "Champagne", price: 189.75, stock: 27 },
+ { sku: "DW-5624", pattern: "Deco Fan", colorway: "Midnight", price: 142.00, stock: 3 },
+ { sku: "DW-6081", pattern: "Linen Weave", colorway: "Alabaster", price: 74.25, stock: 63 }
+ ];
+
+ let sortKey = null, sortDir = 1;
+ const tbody = document.querySelector("#tbl tbody");
+ const search = document.getElementById("search");
+ const countEl = document.getElementById("count");
+ const emptyEl = document.getElementById("empty");
+ const headers = document.querySelectorAll("#tbl thead th");
+
+ function stockClass(n) {
+ if (n <= 5) return "stock-lo";
+ if (n <= 20) return "stock-mid";
+ return "stock-hi";
+ }
+
+ function fmtPrice(p) {
+ return "$" + p.toFixed(2);
+ }
+
+ function render() {
+ const q = search.value.trim().toLowerCase();
+ let rows = data.filter(function (r) {
+ if (!q) return true;
+ return (r.sku + " " + r.pattern + " " + r.colorway + " " + r.price + " " + r.stock)
+ .toLowerCase().indexOf(q) !== -1;
+ });
+
+ if (sortKey) {
+ rows = rows.slice().sort(function (a, b) {
+ const av = a[sortKey], bv = b[sortKey];
+ if (typeof av === "string") {
+ return av.localeCompare(bv) * sortDir;
+ }
+ return (av - bv) * sortDir;
+ });
+ }
+
+ tbody.innerHTML = "";
+ rows.forEach(function (r) {
+ const tr = document.createElement("tr");
+
+ const tds = [
+ { cls: "sku", text: r.sku },
+ { cls: "", text: r.pattern },
+ { cls: "", text: r.colorway },
+ { cls: "num", text: fmtPrice(r.price) }
+ ];
+ tds.forEach(function (c) {
+ const td = document.createElement("td");
+ if (c.cls) td.className = c.cls;
+ td.textContent = c.text;
+ tr.appendChild(td);
+ });
+
+ const tdStock = document.createElement("td");
+ tdStock.className = "num";
+ const pill = document.createElement("span");
+ pill.className = "stock-pill " + stockClass(r.stock);
+ pill.textContent = r.stock + " rolls";
+ tdStock.appendChild(pill);
+ tr.appendChild(tdStock);
+
+ tbody.appendChild(tr);
+ });
+
+ emptyEl.classList.toggle('show', rows.length === 0);
+ countEl.textContent = `${rows.length} of ${data.length} SKUs`;
+
+ headers.forEach(function (th) {
+ const arrow = th.querySelector(".arrow");
+ if (th.dataset.key === sortKey) {
+ arrow.textContent = sortDir === 1 ? "↑" : "↓";
+ arrow.style.transform = sortDir === 1 ? "translateY(-50%)" : "translateY(50%)";
+ } else {
+ arrow.textContent = "↑";
+ arrow.style.transform = "translateY(-50%)";
+ }
+ });
+ }
+
+ headers.forEach(function (th) {
+ th.addEventListener("click", function () {
+ const key = th.dataset.key;
+ if (sortKey === key) {
+ sortDir = -sortDir;
+ } else {
+ sortKey = key;
+ sortDir = 1;
+ }
+ render();
+ });
+ });
+
+ search.addEventListener("input", render);
+ render();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/34ecbeae4e1f/qwen3-14b.png b/data/artifacts/34ecbeae4e1f/qwen3-14b.png
new file mode 100644
index 0000000..64c11d3
Binary files /dev/null and b/data/artifacts/34ecbeae4e1f/qwen3-14b.png differ
diff --git a/data/artifacts/35303b0ef3b8/gpt.html b/data/artifacts/35303b0ef3b8/gpt.html
new file mode 100644
index 0000000..84977b5
--- /dev/null
+++ b/data/artifacts/35303b0ef3b8/gpt.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Color</title>
+<style>
+ body { font-family: sans-serif; margin: 0; display:flex; justify-content:center; align-items:center; height:100vh; }
+</style>
+</head>
+<body>
+<script>
+ document.body.textContent = "Blue.";
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/35303b0ef3b8/gpt.png b/data/artifacts/35303b0ef3b8/gpt.png
new file mode 100644
index 0000000..2bc2eb5
Binary files /dev/null and b/data/artifacts/35303b0ef3b8/gpt.png differ
diff --git a/data/artifacts/35303b0ef3b8/grok.html b/data/artifacts/35303b0ef3b8/grok.html
new file mode 100644
index 0000000..60d44a7
--- /dev/null
+++ b/data/artifacts/35303b0ef3b8/grok.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style>
+body{margin:0;height:100vh;display:flex;align-items:center;justify-content:center;font-family:system-ui,sans-serif;background:#f0f0f0;color:#111}
+p{font-size:1.5rem;margin:0}
+</style>
+</head>
+<body>
+<p>The color is blue.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/35303b0ef3b8/grok.png b/data/artifacts/35303b0ef3b8/grok.png
new file mode 100644
index 0000000..c72462b
Binary files /dev/null and b/data/artifacts/35303b0ef3b8/grok.png differ
diff --git a/data/artifacts/35303b0ef3b8/hermes3-8b.html b/data/artifacts/35303b0ef3b8/hermes3-8b.html
new file mode 100644
index 0000000..4c4657f
--- /dev/null
+++ b/data/artifacts/35303b0ef3b8/hermes3-8b.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Color</title>
+ <style>
+ body {
+ background-color: #1e90ff;
+ color: white;
+ }
+ </style>
+</head>
+<body>
+ <script>
+ alert("The named color is #1e90ff.");
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/35303b0ef3b8/kimi.html b/data/artifacts/35303b0ef3b8/kimi.html
new file mode 100644
index 0000000..e6d035d
--- /dev/null
+++ b/data/artifacts/35303b0ef3b8/kimi.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Color</title>
+<style>
+body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ font-family: system-ui, sans-serif;
+ font-size: 2rem;
+ background: #f0f0f0;
+ color: #333;
+}
+</style>
+</head>
+<body>
+The color is cerulean.
+<script>
+document.addEventListener('DOMContentLoaded', () => {
+ console.log('Color named');
+});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/35303b0ef3b8/kimi.png b/data/artifacts/35303b0ef3b8/kimi.png
new file mode 100644
index 0000000..e3d76f2
Binary files /dev/null and b/data/artifacts/35303b0ef3b8/kimi.png differ
diff --git a/data/artifacts/35303b0ef3b8/qwen25-7b.html b/data/artifacts/35303b0ef3b8/qwen25-7b.html
new file mode 100644
index 0000000..33eec43
--- /dev/null
+++ b/data/artifacts/35303b0ef3b8/qwen25-7b.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html><head><style></style><script>console.log("blue")</script></head><body></body></html>
\ No newline at end of file
diff --git a/data/artifacts/35303b0ef3b8/qwen25-7b.png b/data/artifacts/35303b0ef3b8/qwen25-7b.png
new file mode 100644
index 0000000..16a103c
Binary files /dev/null and b/data/artifacts/35303b0ef3b8/qwen25-7b.png differ
diff --git a/data/artifacts/74df3b61e7ca/qwen25-7b.html b/data/artifacts/74df3b61e7ca/qwen25-7b.html
new file mode 100644
index 0000000..92f54e4
--- /dev/null
+++ b/data/artifacts/74df3b61e7ca/qwen25-7b.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Synthwave Visualizer</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ background-color: #000;
+ }
+ canvas {
+ display: block;
+ width: 100%;
+ height: 100%;
+ }
+ </style>
+</head>
+<body>
+<canvas id="visualizer"></canvas>
+<script>
+const canvas = document.getElementById('visualizer');
+const ctx = canvas.getContext('2d');
+
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+const width = canvas.width;
+const height = canvas.height;
+const step = 10;
+const grid = [];
+
+for (let i = 0; i < width / step; i++) {
+ const x = i * step;
+ for (let j = 0; j < height / step; j++) {
+ const y = j * step;
+ grid.push({x, y});
+ }
+}
+
+const oscillator = new AudioContext().createOscillator();
+oscillator.type = 'sawtooth';
+oscillator.start();
+
+function draw() {
+ ctx.clearRect(0, 0, width, height);
+ for (let i = 0; i < grid.length; i++) {
+ const {x, y} = grid[i];
+ let brightness = Math.abs(Math.sin(x * 0.01 + oscillator.frequency.value * 0.001) + Math.cos(y * 0.01 + oscillator.frequency.value * 0.001));
+ ctx.fillStyle = `hsl(${(x + y) * 5 % 360}, 80%, ${brightness * 90}%)`;
+ ctx.fillRect(x, y, step - 2, step - 2);
+ }
+}
+
+function play() {
+ oscillator.frequency.value = Math.random() * 10 + 4;
+ draw();
+ requestAnimationFrame(play);
+}
+
+play();
+
+const controls = document.createElement('div');
+controls.style.position = 'absolute';
+controls.style.top = '10px';
+controls.style.left = '10px';
+
+const playButton = document.createElement('button');
+playButton.textContent = 'Play';
+playButton.addEventListener('click', () => {
+ oscillator.start();
+ play();
+});
+
+const stopButton = document.createElement('button');
+stopButton.textContent = 'Stop';
+stopButton.addEventListener('click', () => {
+ oscillator.stop();
+});
+
+controls.appendChild(playButton);
+controls.appendChild(stopButton);
+document.body.appendChild(controls);
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/74df3b61e7ca/qwen25-7b.png b/data/artifacts/74df3b61e7ca/qwen25-7b.png
new file mode 100644
index 0000000..f630663
Binary files /dev/null and b/data/artifacts/74df3b61e7ca/qwen25-7b.png differ
diff --git a/data/artifacts/75ec2d6d47f7/gemma3-12b.html b/data/artifacts/75ec2d6d47f7/gemma3-12b.html
new file mode 100644
index 0000000..d90761c
--- /dev/null
+++ b/data/artifacts/75ec2d6d47f7/gemma3-12b.html
@@ -0,0 +1,259 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Gravity Playground</title>
+ <style>
+ body { margin: 0; overflow: hidden; background-color: #111; }
+ canvas { display: block; }
+ </style>
+</head>
+<body>
+ <canvas id="myCanvas"></canvas>
+
+ <script>
+ const canvas = document.getElementById("myCanvas");
+ const ctx = canvas.getContext("2d");
+
+ let width = window.innerWidth;
+ let height = window.innerHeight;
+ canvas.width = width;
+ canvas.height = height;
+
+
+ // Central mass properties
+ const centralMassX = width / 2;
+ const centralMassY = height / 2;
+ const centralMassRadius = 30;
+ const centralMassGravity = 1;
+
+ // Ball properties
+ let balls = [];
+ const maxBalls = 50;
+
+ // Obstacle properties
+ let obstacles = [
+ { x: 100, y: 100, radius: 20 },
+ { x: 300, y: 250, radius: 30 },
+ { x: 450, y: 400, radius: 25 }
+ ];
+
+ // Slingshot properties
+ let slingshot = {
+ x1: width / 2 - 50,
+ y1: height / 2 - 50,
+ x2: width / 2 + 50,
+ y2: height / 2 - 50,
+ ballRadius: 10,
+ launchAngle: 0,
+ launchSpeed: 0,
+ };
+
+ // Trail properties
+ let trails = [];
+ const maxTrails = 100;
+
+
+ function init() {
+ for (let i = 0; i < maxBalls; i++) {
+ balls.push(new Ball());
+ }
+ }
+
+
+
+ class Ball {
+ constructor() {
+ this.x = Math.random() * width;
+ this.y = Math.random() * height;
+ this.radius = Math.random() * 5 + 2;
+ this.vx = (Math.random() - 0.5) * 2;
+ this.vy = (Math.random() - 0.5) * 2;
+ this.color = `rgba(${Math.random()*255}, ${Math.random()*255}, ${Math.random()*255}, 1)`;
+
+ }
+
+ update() {
+ let dx = centralMassX - this.x;
+ let dy = centralMassY - this.y;
+ let distance = Math.sqrt(dx * dx + dy * dy);
+ let force = centralMassGravity / (distance * distance);
+ this.vx += force * dx / distance;
+ this.vy += force * dy / distance;
+
+ this.x += this.vx;
+ this.y += this.vy;
+
+
+ if (this.x - this.radius < 0 || this.x + this.radius > width) {
+ this.vx = -this.vx;
+ }
+ if (this.y - this.radius < 0 || this.y + this.radius > height) {
+ this.vy = -this.vy;
+ }
+
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ }
+ }
+
+
+ class Trail {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.points = [];
+ this.alpha = 1;
+ }
+
+ update() {
+ this.points.push({ x: this.x, y: this.y });
+ if (this.points.length > 20) {
+ this.points.shift();
+ }
+ this.alpha -= 0.01;
+ if(this.alpha < 0){
+ this.alpha = 0; //ensure alpha doesn't go negative
+ }
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.moveTo(this.points[0].x, this.points[0].y);
+ for (let i = 1; i < this.points.length; i++) {
+ ctx.lineTo(this.points[i].x, this.points[i].y);
+ }
+ ctx.strokeStyle = `rgba(${this.color.substring(4, this.color.length-1).split(',').map(Number).join(',')}, ${this.alpha})`;
+ ctx.lineWidth = 2;
+ ctx.stroke();
+
+ }
+ }
+
+ function drawCentralMass() {
+ ctx.beginPath();
+ ctx.arc(centralMassX, centralMassY, centralMassRadius, 0, Math.PI * 2);
+ ctx.fillStyle = "blue";
+ ctx.fill();
+ }
+
+
+ function drawObstacle(obstacle) {
+ ctx.beginPath();
+ ctx.arc(obstacle.x, obstacle.y, obstacle.radius, 0, Math.PI * 2);
+ ctx.fillStyle = 'red';
+ ctx.fill();
+ }
+
+
+
+ function checkCollision(ball) {
+ for (let i = 0; i < obstacles.length; i++) {
+ const obstacle = obstacles[i];
+ const dx = ball.x - obstacle.x;
+ const dy = ball.y - obstacle.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance < ball.radius + obstacle.radius) {
+ // Collision detected! Simple bounce back.
+ let angle = Math.atan2(dy, dx);
+ ball.vx -= Math.cos(angle) * 0.5; // Reduce speed on collision
+ ball.vy -= Math.sin(angle) * 0.5;
+
+ }
+ }
+ }
+
+
+
+ function update() {
+ ctx.clearRect(0, 0, width, height);
+
+ drawCentralMass();
+
+ for (let i = 0; i < balls.length; i++) {
+ const ball = balls[i];
+ ball.update();
+ checkCollision(ball);
+
+ trails.push(new Trail(ball.x, ball.y));
+
+
+ ball.color = `rgba(${Math.random()*255}, ${Math.random()*255}, ${Math.random()*255}, 1)`; //change color on each update
+ ball.draw();
+
+ }
+
+
+ for(let i = trails.length - 1; i >=0 ; --i){
+ trails[i].update();
+ if (trails[i].alpha > 0) {
+ trails[i].draw();
+ } else{
+ trails.splice(i, 1);
+ }
+
+ }
+
+
+ for (let i = 0; i < obstacles.length; i++) {
+ drawObstacle(obstacles[i]);
+ }
+
+
+
+ //Draw slingshot lines
+ ctx.beginPath();
+ ctx.moveTo(slingshot.x1, slingshot.y1);
+ ctx.lineTo(slingshot.x2, slingshot.y2);
+ ctx.strokeStyle = "green";
+ ctx.lineWidth = 3;
+ ctx.stroke();
+
+
+ window.requestAnimationFrame(update);
+
+ }
+
+
+ canvas.addEventListener("mousedown", (e) => {
+ if (e.offsetX > slingshot.x1 && e.offsetX < slingshot.x2 && e.offsetY > slingshot.y1 && e.offsetY < slingshot.y2){
+ slingshot.launchAngle = Math.atan2(e.offsetY - slingshot.y1, e.offsetX - slingshot.x1);
+ slingshot.launchSpeed = 5; //adjust launch speed as needed
+ }
+
+ });
+
+
+ window.addEventListener("mouseup", () => {
+ if (slingshot.launchSpeed > 0) {
+ let newBall = new Ball();
+ newBall.x = slingshot.x2;
+ newBall.y = slingshot.y2;
+ newBall.vx = slingshot.launchSpeed * Math.cos(slingshot.launchAngle);
+ newBall.vy = slingshot.launchSpeed * Math.sin(slingshot.launchAngle);
+
+ balls.push(newBall);
+ slingshot.launchSpeed = 0;
+ }
+ });
+
+
+ window.addEventListener("resize", () => {
+ width = window.innerWidth;
+ height = window.innerHeight;
+ canvas.width = width;
+ canvas.height = height;
+ });
+
+
+
+ init();
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/75ec2d6d47f7/gemma3-12b.png b/data/artifacts/75ec2d6d47f7/gemma3-12b.png
new file mode 100644
index 0000000..71d328a
Binary files /dev/null and b/data/artifacts/75ec2d6d47f7/gemma3-12b.png differ
diff --git a/data/artifacts/75ec2d6d47f7/gpt.html b/data/artifacts/75ec2d6d47f7/gpt.html
new file mode 100644
index 0000000..d2b85a1
--- /dev/null
+++ b/data/artifacts/75ec2d6d47f7/gpt.html
@@ -0,0 +1,531 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<title>Gravity Playground</title>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: #02040a;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ color: #e0e0e0;
+}
+#ui {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ z-index: 5;
+ background: rgba(0,0,0,0.45);
+ padding: 6px 10px;
+ border-radius: 6px;
+ font-size: 12px;
+ line-height: 1.4;
+ pointer-events: none;
+}
+#ui .title {
+ font-weight: 600;
+ font-size: 13px;
+ margin-bottom: 4px;
+}
+#ui .hint {
+ opacity: 0.9;
+}
+#ui .value {
+ font-variant-numeric: tabular-nums;
+}
+#canvas {
+ display: block;
+}
+</style>
+</head>
+<body>
+<div id="ui">
+ <div class="title">Gravity Playground</div>
+ <div class="hint">
+ Drag to slingshot balls.<br>
+ Click empty space to spawn a ball.<br>
+ Obstacles block and bounce balls.<br>
+ <span class="value" id="info"></span>
+ </div>
+</div>
+<canvas id="canvas"></canvas>
+
+<script>
+(function() {
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const infoEl = document.getElementById('info');
+
+ let width = window.innerWidth;
+ let height = window.innerHeight;
+ canvas.width = width;
+ canvas.height = height;
+
+ window.addEventListener('resize', () => {
+ width = window.innerWidth;
+ height = window.innerHeight;
+ canvas.width = width;
+ canvas.height = height;
+ });
+
+ const G = 4000; // gravitational constant (tuned)
+ const centralMass = 2000; // mass at center
+ const centralRadius = 28;
+
+ const balls = [];
+ const maxBalls = 60;
+ const obstacles = [];
+
+ // Simple RNG
+ function rand(min, max) {
+ return min + Math.random() * (max - min);
+ }
+
+ function createObstacles() {
+ obstacles.length = 0;
+ const cx = width / 2;
+ const cy = height / 2;
+ const baseSize = Math.min(width, height) * 0.07;
+
+ // Three rectangular obstacles around center
+ obstacles.push({ x: cx - baseSize * 2.2, y: cy - baseSize * 0.4, w: baseSize * 1.4, h: baseSize * 0.8 });
+ obstacles.push({ x: cx + baseSize * 0.8, y: cy - baseSize * 0.2, w: baseSize * 1.6, h: baseSize * 0.4 });
+ obstacles.push({ x: cx - baseSize * 0.3, y: cy + baseSize * 1.0, w: baseSize * 0.6, h: baseSize * 1.3 });
+ }
+
+ createObstacles();
+ window.addEventListener('resize', createObstacles);
+
+ function addBall(x, y, vx, vy) {
+ if (balls.length >= maxBalls) balls.shift();
+ const radius = rand(4, 9);
+ const mass = radius * radius;
+ const hue = Math.floor(rand(180, 320));
+ balls.push({
+ x, y, vx, vy,
+ radius, mass,
+ color: `hsl(${hue},70%,60%)`,
+ trail: []
+ });
+ }
+
+ // Initial random balls
+ (function seedBalls() {
+ const cx = width / 2;
+ const cy = height / 2;
+ for (let i = 0; i < 18; i++) {
+ const angle = rand(0, Math.PI * 2);
+ const dist = rand(80, Math.min(width, height) * 0.4);
+ const x = cx + Math.cos(angle) * dist;
+ const y = cy + Math.sin(angle) * dist;
+ // approximate orbital velocity
+ const speed = Math.sqrt(G * centralMass / dist) * 0.25;
+ const vx = -Math.sin(angle) * speed * (0.6 + Math.random() * 0.5);
+ const vy = Math.cos(angle) * speed * (0.6 + Math.random() * 0.5);
+ addBall(x, y, vx, vy);
+ }
+ })();
+
+ // Input handling
+ let dragging = false;
+ let dragStart = null;
+ let lastMouse = { x: 0, y: 0 };
+ let dragBall = null;
+ const maxInitialSpeed = 1200;
+
+ function getBallAt(x, y) {
+ for (let i = balls.length - 1; i >= 0; i--) {
+ const b = balls[i];
+ const dx = x - b.x;
+ const dy = y - b.y;
+ if (dx * dx + dy * dy <= b.radius * b.radius * 2.5) return b;
+ }
+ return null;
+ }
+
+ canvas.addEventListener('mousedown', e => {
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+ dragging = true;
+ dragStart = { x, y, time: performance.now() };
+ dragBall = getBallAt(x, y);
+ lastMouse.x = x;
+ lastMouse.y = y;
+ });
+
+ canvas.addEventListener('mousemove', e => {
+ if (!dragging) return;
+ const rect = canvas.getBoundingClientRect();
+ lastMouse.x = e.clientX - rect.left;
+ lastMouse.y = e.clientY - rect.top;
+ });
+
+ function finalizeDrag(eventType) {
+ if (!dragging || !dragStart) return;
+ dragging = false;
+
+ const x = lastMouse.x;
+ const y = lastMouse.y;
+ const dx = dragStart.x - x;
+ const dy = dragStart.y - y;
+ const dist = Math.hypot(dx, dy);
+
+ const dt = (performance.now() - dragStart.time) / 1000;
+ // compute velocity proportional to drag distance / time, clamped
+ let speed = dist / (dt + 0.04) * 0.6;
+ if (speed > maxInitialSpeed) speed = maxInitialSpeed;
+ const ang = Math.atan2(dy, dx);
+ const vx = Math.cos(ang) * speed;
+ const vy = Math.sin(ang) * speed;
+
+ if (dist < 4) {
+ // simple click: spawn a new ball
+ const angle = rand(0, Math.PI * 2);
+ addBall(dragStart.x, dragStart.y, Math.cos(angle) * 60, Math.sin(angle) * 60);
+ } else {
+ if (dragBall) {
+ dragBall.x = dragStart.x;
+ dragBall.y = dragStart.y;
+ dragBall.vx = vx * 0.018;
+ dragBall.vy = vy * 0.018;
+ dragBall.trail.length = 0;
+ } else {
+ addBall(dragStart.x, dragStart.y, vx * 0.018, vy * 0.018);
+ }
+ }
+ dragStart = null;
+ dragBall = null;
+ }
+
+ canvas.addEventListener('mouseup', () => finalizeDrag('mouse'));
+ canvas.addEventListener('mouseleave', () => { if (dragging) finalizeDrag('leave'); });
+
+ // Touch support
+ canvas.addEventListener('touchstart', e => {
+ e.preventDefault();
+ if (e.touches.length > 1) return;
+ const rect = canvas.getBoundingClientRect();
+ const t = e.touches[0];
+ const x = t.clientX - rect.left;
+ const y = t.clientY - rect.top;
+ dragging = true;
+ dragStart = { x, y, time: performance.now() };
+ dragBall = getBallAt(x, y);
+ lastMouse.x = x;
+ lastMouse.y = y;
+ }, { passive: false });
+
+ canvas.addEventListener('touchmove', e => {
+ e.preventDefault();
+ if (!dragging) return;
+ const rect = canvas.getBoundingClientRect();
+ const t = e.touches[0];
+ lastMouse.x = t.clientX - rect.left;
+ lastMouse.y = t.clientY - rect.top;
+ }, { passive: false });
+
+ canvas.addEventListener('touchend', e => {
+ e.preventDefault();
+ finalizeDrag('touch');
+ }, { passive: false });
+
+ // Physics helpers
+ function resolveBallCollision(a, b) {
+ const dx = b.x - a.x;
+ const dy = b.y - a.y;
+ const dist = Math.hypot(dx, dy) || 0.0001;
+ const nx = dx / dist;
+ const ny = dy / dist;
+
+ // Separate overlapping
+ const overlap = a.radius + b.radius - dist;
+ if (overlap > 0) {
+ const totalMass = a.mass + b.mass;
+ const pushA = overlap * (b.mass / totalMass);
+ const pushB = overlap * (a.mass / totalMass);
+ a.x -= nx * pushA;
+ a.y -= ny * pushA;
+ b.x += nx * pushB;
+ b.y += ny * pushB;
+ }
+
+ const tx = -ny;
+ const ty = nx;
+
+ const va_n = a.vx * nx + a.vy * ny;
+ const va_t = a.vx * tx + a.vy * ty;
+ const vb_n = b.vx * nx + b.vy * ny;
+ const vb_t = b.vx * tx + b.vy * ty;
+
+ // 1D elastic collision along normal
+ const m1 = a.mass, m2 = b.mass;
+ const va_n_after = (va_n * (m1 - m2) + 2 * m2 * vb_n) / (m1 + m2);
+ const vb_n_after = (vb_n * (m2 - m1) + 2 * m1 * va_n) / (m1 + m2);
+
+ a.vx = va_n_after * nx + va_t * tx;
+ a.vy = va_n_after * ny + va_t * ty;
+ b.vx = vb_n_after * nx + vb_t * tx;
+ b.vy = vb_n_after * ny + vb_t * ty;
+ }
+
+ function clamp(v, min, max) {
+ return v < min ? min : (v > max ? max : v);
+ }
+
+ function ballObstacleCollision(b, o) {
+ // Closest point on AABB
+ const closestX = clamp(b.x, o.x, o.x + o.w);
+ const closestY = clamp(b.y, o.y, o.y + o.h);
+ const dx = b.x - closestX;
+ const dy = b.y - closestY;
+ const distSq = dx * dx + dy * dy;
+ if (distSq > b.radius * b.radius) return;
+
+ const dist = Math.sqrt(distSq) || 0.0001;
+ const nx = dx / dist;
+ const ny = dy / dist;
+
+ // Push ball out
+ const overlap = b.radius - dist;
+ b.x += nx * overlap;
+ b.y += ny * overlap;
+
+ // Reflect velocity with some friction
+ const velN = b.vx * nx + b.vy * ny;
+ const velT = b.vx * (-ny) + b.vy * nx;
+ const elasticity = 0.95;
+ const friction = 0.9;
+ const velNAfter = -velN * elasticity;
+ const velTAfter = velT * friction;
+ b.vx = velNAfter * nx + velTAfter * (-ny);
+ b.vy = velNAfter * ny + velTAfter * nx;
+ }
+
+ let lastTime = performance.now();
+
+ function step(timestamp) {
+ const dt = Math.min((timestamp - lastTime) / 1000, 0.033);
+ lastTime = timestamp;
+
+ const cx = width / 2;
+ const cy = height / 2;
+
+ // Physics integration
+ for (let i = 0; i < balls.length; i++) {
+ const b = balls[i];
+
+ // Gravity toward center
+ const dx = cx - b.x;
+ const dy = cy - b.y;
+ const distSq = dx * dx + dy * dy;
+ const dist = Math.sqrt(distSq) || 0.0001;
+ const force = (G * centralMass * b.mass) / (distSq + 2000);
+ const ax = force * dx / dist / b.mass;
+ const ay = force * dy / dist / b.mass;
+
+ b.vx += ax * dt;
+ b.vy += ay * dt;
+
+ // Light drag to stabilize
+ const drag = 0.999;
+ b.vx *= drag;
+ b.vy *= drag;
+
+ b.x += b.vx * dt;
+ b.y += b.vy * dt;
+
+ // Boundary bounce
+ if (b.x - b.radius < 0) {
+ b.x = b.radius;
+ b.vx = Math.abs(b.vx) * 0.8;
+ } else if (b.x + b.radius > width) {
+ b.x = width - b.radius;
+ b.vx = -Math.abs(b.vx) * 0.8;
+ }
+
+ if (b.y - b.radius < 0) {
+ b.y = b.radius;
+ b.vy = Math.abs(b.vy) * 0.8;
+ } else if (b.y + b.radius > height) {
+ b.y = height - b.radius;
+ b.vy = -Math.abs(b.vy) * 0.8;
+ }
+
+ // Central mass collision (elastic reflect)
+ const dcx = b.x - cx;
+ const dcy = b.y - cy;
+ const dcen = Math.hypot(dcx, dcy);
+ if (dcen < centralRadius + b.radius * 0.85) {
+ const nx = dcx / (dcen || 0.0001);
+ const ny = dcy / (dcen || 0.0001);
+ const overlap = centralRadius + b.radius * 0.85 - dcen;
+ b.x += nx * overlap;
+ b.y += ny * overlap;
+ const velN = b.vx * nx + b.vy * ny;
+ const velT = b.vx * (-ny) + b.vy * nx;
+ const velNAfter = -velN * 0.95;
+ b.vx = velNAfter * nx + velT * (-ny);
+ b.vy = velNAfter * ny + velT * nx;
+ }
+
+ // Obstacles
+ for (let j = 0; j < obstacles.length; j++) {
+ ballObstacleCollision(b, obstacles[j]);
+ }
+
+ // Update trail
+ b.trail.push({ x: b.x, y: b.y });
+ const maxTrail = 40;
+ if (b.trail.length > maxTrail) b.trail.shift();
+ }
+
+ // Ball-ball collisions
+ for (let i = 0; i < balls.length; i++) {
+ for (let j = i + 1; j < balls.length; j++) {
+ const a = balls[i];
+ const b = balls[j];
+ const dx = b.x - a.x;
+ const dy = b.y - a.y;
+ const minDist = a.radius + b.radius;
+ if (dx * dx + dy * dy <= minDist * minDist) {
+ resolveBallCollision(a, b);
+ }
+ }
+ }
+
+ // Render
+ ctx.clearRect(0, 0, width, height);
+
+ // Background gradient
+ const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(width, height));
+ grad.addColorStop(0, '#050814');
+ grad.addColorStop(1, '#010208');
+ ctx.fillStyle = grad;
+ ctx.fillRect(0, 0, width, height);
+
+ // Central mass glow
+ const glowGrad = ctx.createRadialGradient(cx, cy, 0, cx, cy, centralRadius * 4);
+ glowGrad.addColorStop(0, 'rgba(255,255,255,0.9)');
+ glowGrad.addColorStop(0.2, 'rgba(255,240,200,0.8)');
+ glowGrad.addColorStop(0.5, 'rgba(200,120,40,0.3)');
+ glowGrad.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = glowGrad;
+ ctx.beginPath();
+ ctx.arc(cx, cy, centralRadius * 3.5, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Central solid core
+ ctx.beginPath();
+ ctx.fillStyle = '#ffe9b6';
+ ctx.arc(cx, cy, centralRadius, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Obstacles
+ ctx.save();
+ ctx.lineWidth = 2;
+ obstacles.forEach(o => {
+ const og = ctx.createLinearGradient(o.x, o.y, o.x + o.w, o.y + o.h);
+ og.addColorStop(0, 'rgba(30,60,100,0.95)');
+ og.addColorStop(1, 'rgba(10,20,40,0.95)');
+ ctx.fillStyle = og;
+ ctx.strokeStyle = 'rgba(130,180,255,0.9)';
+ ctx.beginPath();
+ ctx.rect(o.x, o.y, o.w, o.h);
+ ctx.fill();
+ ctx.stroke();
+ });
+ ctx.restore();
+
+ // Trails and balls
+ for (let i = 0; i < balls.length; i++) {
+ const b = balls[i];
+
+ // Trail
+ if (b.trail.length > 1) {
+ ctx.beginPath();
+ for (let t = 0; t < b.trail.length - 1; t++) {
+ const p = b.trail[t];
+ const np = b.trail[t + 1];
+ const alpha = t / b.trail.length;
+ ctx.strokeStyle = b.color.replace('60%)', (30 + alpha * 40) + '%)').replace('hsl', 'hsla').replace('%)', `%,${alpha * 0.4})`);
+ ctx.lineWidth = b.radius * 0.6 * (0.2 + alpha * 0.8);
+ ctx.beginPath();
+ ctx.moveTo(p.x, p.y);
+ ctx.lineTo(np.x, np.y);
+ ctx.stroke();
+ }
+ }
+
+ // Ball glow
+ ctx.beginPath();
+ const bg = ctx.createRadialGradient(b.x, b.y, b.radius * 0.1, b.x, b.y, b.radius * 2.0);
+ bg.addColorStop(0, b.color.replace('60%)', '75%)'));
+ bg.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = bg;
+ ctx.arc(b.x, b.y, b.radius * 1.8, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Ball core
+ ctx.beginPath();
+ ctx.fillStyle = b.color;
+ ctx.strokeStyle = 'rgba(255,255,255,0.45)';
+ ctx.lineWidth = 1;
+ ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.stroke();
+ }
+
+ // Drag indicator
+ if (dragging && dragStart) {
+ ctx.save();
+ const dx = lastMouse.x - dragStart.x;
+ const dy = lastMouse.y - dragStart.y;
+ const dist = Math.hypot(dx, dy);
+ const dirX = dx / (dist || 1);
+ const dirY = dy / (dist || 1);
+ const arrowLen = Math.min(dist, 120);
+
+ // Line
+ ctx.strokeStyle = 'rgba(230,240,255,0.9)';
+ ctx.lineWidth = 2;
+ ctx.setLineDash([4, 4]);
+ ctx.beginPath();
+ ctx.moveTo(dragStart.x, dragStart.y);
+ ctx.lineTo(dragStart.x + dirX * arrowLen, dragStart.y + dirY * arrowLen);
+ ctx.stroke();
+
+ // Arrow head
+ const headSize = 8;
+ const endX = dragStart.x + dirX * arrowLen;
+ const endY = dragStart.y + dirY * arrowLen;
+ ctx.setLineDash([]);
+ ctx.beginPath();
+ ctx.moveTo(endX, endY);
+ ctx.lineTo(endX - dirX * headSize - dirY * headSize * 0.6, endY - dirY * headSize + dirX * headSize * 0.6);
+ ctx.lineTo(endX - dirX * headSize + dirY * headSize * 0.6, endY - dy / dist * headSize - dirX * headSize * 0.6);
+ ctx.closePath();
+ ctx.fillStyle = 'rgba(230,240,255,0.9)';
+ ctx.fill();
+
+ // Start circle
+ ctx.beginPath();
+ ctx.lineWidth = 1.5;
+ ctx.strokeStyle = 'rgba(255,255,255,0.8)';
+ ctx.arc(dragStart.x, dragStart.y, 5, 0, Math.PI * 2);
+ ctx.stroke();
+ ctx.restore();
+ }
+
+ infoEl.textContent = `Balls: ${balls.length.toString().padStart(2,' ')} · Obstacles: ${obstacles.length}`;
+
+ requestAnimationFrame(step);
+ }
+
+ requestAnimationFrame(step);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/75ec2d6d47f7/gpt.png b/data/artifacts/75ec2d6d47f7/gpt.png
new file mode 100644
index 0000000..989cd29
Binary files /dev/null and b/data/artifacts/75ec2d6d47f7/gpt.png differ
diff --git a/data/artifacts/75ec2d6d47f7/grok.html b/data/artifacts/75ec2d6d47f7/grok.html
new file mode 100644
index 0000000..60353ec
--- /dev/null
+++ b/data/artifacts/75ec2d6d47f7/grok.html
@@ -0,0 +1,517 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Gravity Playground</title>
+<style>
+*{margin:0;padding:0;box-sizing:border-box}
+html,body{width:100%;height:100%;overflow:hidden;background:#0a0a12;font-family:system-ui,sans-serif}
+canvas{display:block;width:100%;height:100%;cursor:crosshair}
+#hud{position:fixed;top:12px;left:12px;color:rgba(200,220,255,.7);font-size:13px;pointer-events:none;line-height:1.6;text-shadow:0 1px 3px #000}
+#hud b{color:rgba(140,200,255,.95)}
+</style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<div id="hud"><b>Gravity Playground</b><br>Drag empty space to slingshot · Balls collide & orbit<br>Obstacles deflect · Trails fade · Click ball to remove</div>
+<script>
+(() => {
+const canvas = document.getElementById('c');
+const ctx = canvas.getContext('2d');
+let W, H, CX, CY, dpr;
+
+function resize() {
+ dpr = Math.min(window.devicePixelRatio || 1, 2);
+ W = window.innerWidth;
+ H = window.innerHeight;
+ canvas.width = W * dpr;
+ canvas.height = H * dpr;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ CX = W / 2;
+ CY = H / 2;
+}
+resize();
+window.addEventListener('resize', resize);
+
+const G = 4200;
+const CENTRAL_MASS = 180;
+const CENTRAL_R = 28;
+const FRICTION = 0.9994;
+const MAX_BALLS = 40;
+const TRAIL_LEN = 48;
+
+const balls = [];
+const obstacles = [];
+const sparks = [];
+
+// Obstacles: circles and rectangles
+function initObstacles() {
+ obstacles.length = 0;
+ const configs = [
+ { type: 'circle', x: CX - 220, y: CY - 140, r: 36, hue: 200 },
+ { type: 'circle', x: CX + 250, y: CY + 100, r: 48, hue: 320 },
+ { type: 'circle', x: CX + 180, y: CY - 200, r: 28, hue: 160 },
+ { type: 'rect', x: CX - 320, y: CY + 80, w: 70, h: 70, rot: 0.4, hue: 40 },
+ { type: 'rect', x: CX + 80, y: CY + 200, w: 90, h: 36, rot: -0.3, hue: 280 },
+ ];
+ for (const c of configs) obstacles.push({ ...c, vx: 0, vy: 0 });
+}
+initObstacles();
+window.addEventListener('resize', () => { initObstacles(); });
+
+function randHue() { return Math.random() * 360; }
+
+function createBall(x, y, vx, vy, r) {
+ r = r || 8 + Math.random() * 10;
+ return {
+ x, y, vx, vy, r,
+ mass: r * r * 0.15,
+ hue: randHue(),
+ trail: [],
+ life: 1
+ };
+}
+
+// Seed a few orbiting balls
+function seed() {
+ for (let i = 0; i < 5; i++) {
+ const ang = (i / 5) * Math.PI * 2;
+ const dist = 160 + i * 35;
+ const speed = Math.sqrt(G * CENTRAL_MASS / dist) * 0.92;
+ balls.push(createBall(
+ CX + Math.cos(ang) * dist,
+ CY + Math.sin(ang) * dist,
+ -Math.sin(ang) * speed,
+ Math.cos(ang) * speed,
+ 9 + i * 1.5
+ ));
+ }
+}
+seed();
+
+// Input
+let dragging = false;
+let dragStart = null;
+let dragCurr = null;
+let mouse = { x: 0, y: 0 };
+
+function pos(e) {
+ if (e.touches) {
+ return { x: e.touches[0].clientX, y: e.touches[0].clientY };
+ }
+ return { x: e.clientX, y: e.clientY };
+}
+
+canvas.addEventListener('mousedown', onDown);
+canvas.addEventListener('touchstart', onDown, { passive: false });
+canvas.addEventListener('mousemove', onMove);
+canvas.addEventListener('touchmove', onMove, { passive: false });
+canvas.addEventListener('mouseup', onUp);
+canvas.addEventListener('touchend', onUp);
+canvas.addEventListener('mouseleave', onUp);
+
+function onDown(e) {
+ e.preventDefault();
+ const p = pos(e);
+ mouse = p;
+ // Check if clicking an existing ball to remove
+ for (let i = balls.length - 1; i >= 0; i--) {
+ const b = balls[i];
+ const dx = p.x - b.x, dy = p.y - b.y;
+ if (dx * dx + dy * dy < b.r * b.r * 2.5) {
+ // Burst sparks
+ for (let k = 0; k < 12; k++) {
+ const a = Math.random() * Math.PI * 2;
+ const sp = 1 + Math.random() * 4;
+ sparks.push({ x: b.x, y: b.y, vx: Math.cos(a) * sp, vy: Math.sin(a) * sp, life: 1, hue: b.hue });
+ }
+ balls.splice(i, 1);
+ return;
+ }
+ }
+ dragging = true;
+ dragStart = p;
+ dragCurr = p;
+}
+
+function onMove(e) {
+ e.preventDefault();
+ const p = pos(e);
+ mouse = p;
+ if (dragging) dragCurr = p;
+}
+
+function onUp(e) {
+ if (!dragging) return;
+ dragging = false;
+ if (!dragStart || !dragCurr) return;
+ const dx = dragStart.x - dragCurr.x;
+ const dy = dragStart.y - dragCurr.y;
+ const dist = Math.hypot(dx, dy);
+ if (dist < 6) { dragStart = dragCurr = null; return; }
+ const power = Math.min(dist * 0.14, 22);
+ const ang = Math.atan2(dy, dx);
+ if (balls.length >= MAX_BALLS) balls.shift();
+ balls.push(createBall(
+ dragStart.x, dragStart.y,
+ Math.cos(ang) * power,
+ Math.sin(ang) * power
+ ));
+ dragStart = dragCurr = null;
+}
+
+// Physics
+function gravityOn(b) {
+ const dx = CX - b.x;
+ const dy = CY - b.y;
+ const d2 = dx * dx + dy * dy;
+ const d = Math.sqrt(d2);
+ if (d < CENTRAL_R + b.r) {
+ // Soft bounce off central mass
+ const nx = dx / d, ny = dy / d;
+ const overlap = CENTRAL_R + b.r - d;
+ b.x -= nx * overlap;
+ b.y -= ny * overlap;
+ const vn = b.vx * nx + b.vy * ny;
+ if (vn > 0) {
+ b.vx -= 1.7 * vn * nx;
+ b.vy -= 1.7 * vn * ny;
+ }
+ return;
+ }
+ const f = (G * CENTRAL_MASS) / (d2 + 80);
+ b.vx += (dx / d) * f * 0.016;
+ b.vy += (dy / d) * f * 0.016;
+}
+
+function collideBalls(a, b) {
+ const dx = b.x - a.x;
+ const dy = b.y - a.y;
+ const dist = Math.hypot(dx, dy);
+ const minD = a.r + b.r;
+ if (dist === 0 || dist >= minD) return;
+ const nx = dx / dist, ny = dy / dist;
+ // Separate
+ const overlap = minD - dist;
+ const totalMass = a.mass + b.mass;
+ a.x -= nx * overlap * (b.mass / totalMass);
+ a.y -= ny * overlap * (b.mass / totalMass);
+ b.x += nx * overlap * (a.mass / totalMass);
+ b.y += ny * overlap * (a.mass / totalMass);
+ // Elastic collision
+ const dvx = a.vx - b.vx;
+ const dvy = a.vy - b.vy;
+ const dvn = dvx * nx + dvy * ny;
+ if (dvn > 0) return;
+ const e = 0.92;
+ const j = -(1 + e) * dvn / (1 / a.mass + 1 / b.mass);
+ a.vx += (j / a.mass) * nx;
+ a.vy += (j / a.mass) * ny;
+ b.vx -= (j / b.mass) * nx;
+ b.vy -= (j / b.mass) * ny;
+ // Sparks
+ if (Math.abs(j) > 8) {
+ const mx = (a.x + b.x) / 2, my = (a.y + b.y) / 2;
+ for (let k = 0; k < 4; k++) {
+ const ang = Math.atan2(ny, nx) + (Math.random() - 0.5);
+ sparks.push({
+ x: mx, y: my,
+ vx: Math.cos(ang) * 2, vy: Math.sin(ang) * 2,
+ life: 0.8, hue: (a.hue + b.hue) / 2
+ });
+ }
+ }
+}
+
+function collideObstacle(b, o) {
+ if (o.type === 'circle') {
+ const dx = b.x - o.x, dy = b.y - o.y;
+ const dist = Math.hypot(dx, dy);
+ const minD = b.r + o.r;
+ if (dist >= minD || dist === 0) return;
+ const nx = dx / dist, ny = dy / dist;
+ const overlap = minD - dist;
+ b.x += nx * overlap;
+ b.y += ny * overlap;
+ const vn = b.vx * nx + b.vy * ny;
+ if (vn < 0) {
+ b.vx -= 1.75 * vn * nx;
+ b.vy -= 1.75 * vn * ny;
+ }
+ } else if (o.type === 'rect') {
+ // Transform ball into rect local space
+ const cos = Math.cos(-o.rot), sin = Math.sin(-o.rot);
+ const lx = (b.x - o.x) * cos - (b.y - o.y) * sin;
+ const ly = (b.x - o.x) * sin + (b.y - o.y) * cos;
+ const hw = o.w / 2, hh = o.h / 2;
+ const cx = Math.max(-hw, Math.min(hw, lx));
+ const cy = Math.max(-hh, Math.min(hh, ly));
+ const dx = lx - cx, dy = ly - cy;
+ const dist = Math.hypot(dx, dy);
+ if (dist >= b.r || dist === 0) {
+ // Inside check
+ if (Math.abs(lx) < hw && Math.abs(ly) < hh) {
+ // Push out along nearest edge
+ const ox = hw - Math.abs(lx);
+ const oy = hh - Math.abs(ly);
+ let nx = 0, ny = 0;
+ if (ox < oy) { nx = lx > 0 ? 1 : -1; }
+ else { ny = ly > 0 ? 1 : -1; }
+ // Back to world
+ const c2 = Math.cos(o.rot), s2 = Math.sin(o.rot);
+ const wnx = nx * c2 - ny * s2;
+ const wny = nx * s2 + ny * c2;
+ b.x += wnx * (b.r + (ox < oy ? ox : oy));
+ b.y += wny * (b.r + (ox < oy ? ox : oy));
+ const vn = b.vx * wnx + b.vy * wny;
+ if (vn < 0) { b.vx -= 1.75 * vn * wnx; b.vy -= 1.75 * vn * wny; }
+ }
+ return;
+ }
+ const nx_l = dx / dist, ny_l = dy / dist;
+ const c2 = Math.cos(o.rot), s2 = Math.sin(o.rot);
+ const nx = nx_l * c2 - ny_l * s2;
+ const ny = nx_l * s2 + ny_l * c2;
+ const overlap = b.r - dist;
+ b.x += nx * overlap;
+ b.y += ny * overlap;
+ const vn = b.vx * nx + b.vy * ny;
+ if (vn < 0) {
+ b.vx -= 1.75 * vn * nx;
+ b.vy -= 1.75 * vn * ny;
+ }
+ }
+}
+
+function update(dt) {
+ const steps = 2;
+ const h = dt / steps;
+ for (let s = 0; s < steps; s++) {
+ for (const b of balls) {
+ gravityOn(b);
+ b.vx *= FRICTION;
+ b.vy *= FRICTION;
+ b.x += b.vx * h * 60;
+ b.y += b.vy * h * 60;
+
+ // Soft walls
+ const m = b.r + 4;
+ if (b.x < m) { b.x = m; b.vx *= -0.7; }
+ if (b.x > W - m) { b.x = W - m; b.vx *= -0.7; }
+ if (b.y < m) { b.y = m; b.vy *= -0.7; }
+ if (b.y > H - m) { b.y = H - m; b.vy *= -0.7; }
+
+ for (const o of obstacles) collideObstacle(b, o);
+ }
+ for (let i = 0; i < balls.length; i++) {
+ for (let j = i + 1; j < balls.length; j++) {
+ collideBalls(balls[i], balls[j]);
+ }
+ }
+ }
+
+ for (const b of balls) {
+ b.trail.push({ x: b.x, y: b.y });
+ if (b.trail.length > TRAIL_LEN) b.trail.shift();
+ }
+
+ for (let i = sparks.length - 1; i >= 0; i--) {
+ const p = sparks[i];
+ p.x += p.vx;
+ p.y += p.vy;
+ p.vx *= 0.96;
+ p.vy *= 0.96;
+ p.life -= 0.03;
+ if (p.life <= 0) sparks.splice(i, 1);
+ }
+}
+
+// Render
+function draw() {
+ // Fade background for trails effect via translucent clear
+ ctx.fillStyle = 'rgba(8, 8, 16, 0.28)';
+ ctx.fillRect(0, 0, W, H);
+
+ // Starfield dots (static-ish)
+ ctx.fillStyle = 'rgba(255,255,255,0.15)';
+ for (let i = 0; i < 60; i++) {
+ const sx = ((i * 97 + 30) % W);
+ const sy = ((i * 53 + 70) % H);
+ ctx.fillRect(sx, sy, 1.2, 1.2);
+ }
+
+ // Central mass glow
+ const grd = ctx.createRadialGradient(CX, CY, 4, CX, CY, CENTRAL_R * 3.5);
+ grd.addColorStop(0, 'rgba(255, 220, 120, 0.95)');
+ grd.addColorStop(0.25, 'rgba(255, 160, 40, 0.55)');
+ grd.addColorStop(0.55, 'rgba(200, 60, 20, 0.15)');
+ grd.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.beginPath();
+ ctx.arc(CX, CY, CENTRAL_R * 3.5, 0, Math.PI * 2);
+ ctx.fillStyle = grd;
+ ctx.fill();
+
+ // Core
+ const core = ctx.createRadialGradient(CX - 6, CY - 6, 2, CX, CY, CENTRAL_R);
+ core.addColorStop(0, '#fff6d0');
+ core.addColorStop(0.4, '#ffaa30');
+ core.addColorStop(1, '#c04010');
+ ctx.beginPath();
+ ctx.arc(CX, CY, CENTRAL_R, 0, Math.PI * 2);
+ ctx.fillStyle = core;
+ ctx.fill();
+ ctx.strokeStyle = 'rgba(255,200,80,0.4)';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+
+ // Accretion ring
+ ctx.beginPath();
+ ctx.ellipse(CX, CY, CENTRAL_R * 1.55, CENTRAL_R * 0.45, 0.3, 0, Math.PI * 2);
+ ctx.strokeStyle = 'rgba(255,180,60,0.25)';
+ ctx.lineWidth = 3;
+ ctx.stroke();
+
+ // Obstacles
+ for (const o of obstacles) {
+ ctx.save();
+ if (o.type === 'circle') {
+ const g = ctx.createRadialGradient(o.x - o.r * 0.3, o.y - o.r * 0.3, 2, o.x, o.y, o.r);
+ g.addColorStop(0, `hsla(${o.hue}, 70%, 65%, 0.85)`);
+ g.addColorStop(1, `hsla(${o.hue}, 80%, 30%, 0.9)`);
+ ctx.beginPath();
+ ctx.arc(o.x, o.y, o.r, 0, Math.PI * 2);
+ ctx.fillStyle = g;
+ ctx.fill();
+ ctx.strokeStyle = `hsla(${o.hue}, 90%, 70%, 0.5)`;
+ ctx.lineWidth = 2;
+ ctx.stroke();
+ } else {
+ ctx.translate(o.x, o.y);
+ ctx.rotate(o.rot);
+ const g = ctx.createLinearGradient(-o.w / 2, -o.h / 2, o.w / 2, o.h / 2);
+ g.addColorStop(0, `hsla(${o.hue}, 65%, 55%, 0.85)`);
+ g.addColorStop(1, `hsla(${o.hue}, 75%, 28%, 0.9)`);
+ ctx.fillStyle = g;
+ ctx.strokeStyle = `hsla(${o.hue}, 90%, 70%, 0.5)`;
+ ctx.lineWidth = 2;
+ roundRect(ctx, -o.w / 2, -o.h / 2, o.w, o.h, 8);
+ ctx.fill();
+ ctx.stroke();
+ }
+ ctx.restore();
+ }
+
+ // Trails
+ for (const b of balls) {
+ if (b.trail.length < 2) continue;
+ ctx.beginPath();
+ ctx.moveTo(b.trail[0].x, b.trail[0].y);
+ for (let i = 1; i < b.trail.length; i++) {
+ ctx.lineTo(b.trail[i].x, b.trail[i].y);
+ }
+ ctx.strokeStyle = `hsla(${b.hue}, 85%, 60%, 0.35)`;
+ ctx.lineWidth = Math.max(1.5, b.r * 0.35);
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+ ctx.stroke();
+ }
+
+ // Balls
+ for (const b of balls) {
+ const g = ctx.createRadialGradient(b.x - b.r * 0.35, b.y - b.r * 0.35, 1, b.x, b.y, b.r);
+ g.addColorStop(0, `hsla(${b.hue}, 90%, 80%, 1)`);
+ g.addColorStop(0.5, `hsla(${b.hue}, 85%, 55%, 1)`);
+ g.addColorStop(1, `hsla(${b.hue}, 90%, 35%, 1)`);
+ ctx.beginPath();
+ ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
+ ctx.fillStyle = g;
+ ctx.fill();
+ ctx.strokeStyle = `hsla(${b.hue}, 100%, 75%, 0.45)`;
+ ctx.lineWidth = 1.5;
+ ctx.stroke();
+ }
+
+ // Sparks
+ for (const p of sparks) {
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, 2 * p.life, 0, Math.PI * 2);
+ ctx.fillStyle = `hsla(${p.hue}, 100%, 70%, ${p.life})`;
+ ctx.fill();
+ }
+
+ // Slingshot aim
+ if (dragging && dragStart && dragCurr) {
+ const dx = dragStart.x - dragCurr.x;
+ const dy = dragStart.y - dragCurr.y;
+ // Rubber band
+ ctx.beginPath();
+ ctx.moveTo(dragStart.x, dragStart.y);
+ ctx.lineTo(dragCurr.x, dragCurr.y);
+ ctx.strokeStyle = 'rgba(180, 220, 255, 0.55)';
+ ctx.lineWidth = 2;
+ ctx.setLineDash([6, 5]);
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ // Predicted direction arrow
+ const power = Math.min(Math.hypot(dx, dy) * 0.14, 22);
+ const ang = Math.atan2(dy, dx);
+ const ax = dragStart.x + Math.cos(ang) * power * 3.5;
+ const ay = dragStart.y + Math.sin(ang) * power * 3.5;
+ ctx.beginPath();
+ ctx.moveTo(dragStart.x, dragStart.y);
+ ctx.lineTo(ax, ay);
+ ctx.strokeStyle = 'rgba(120, 200, 255, 0.7)';
+ ctx.lineWidth = 2.5;
+ ctx.stroke();
+
+ // Ghost ball
+ ctx.beginPath();
+ ctx.arc(dragStart.x, dragStart.y, 10, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(150, 210, 255, 0.35)';
+ ctx.fill();
+ ctx.strokeStyle = 'rgba(180, 230, 255, 0.8)';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+
+ // Arrow head
+ const ah = ang + Math.PI;
+ ctx.beginPath();
+ ctx.moveTo(ax, ay);
+ ctx.lineTo(ax + Math.cos(ah + 0.4) * 12, ay + Math.sin(ah + 0.4) * 12);
+ ctx.lineTo(ax + Math.cos(ah - 0.4) * 12, ay + Math.sin(ah - 0.4) * 12);
+ ctx.closePath();
+ ctx.fillStyle = 'rgba(120, 200, 255, 0.8)';
+ ctx.fill();
+ }
+}
+
+function roundRect(ctx, x, y, w, h, r) {
+ ctx.beginPath();
+ ctx.moveTo(x + r, y);
+ ctx.arcTo(x + w, y, x + w, y + h, r);
+ ctx.arcTo(x + w, y + h, x, y + h, r);
+ ctx.arcTo(x, y + h, x, y, r);
+ ctx.arcTo(x, y, x + w, y, r);
+ ctx.closePath();
+}
+
+// Main loop
+let last = performance.now();
+function frame(now) {
+ let dt = (now - last) / 1000;
+ last = now;
+ if (dt > 0.05) dt = 0.05;
+ update(dt);
+ draw();
+ requestAnimationFrame(frame);
+}
+// Initial opaque clear
+ctx.fillStyle = '#080810';
+ctx.fillRect(0, 0, W, H);
+requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/75ec2d6d47f7/grok.png b/data/artifacts/75ec2d6d47f7/grok.png
new file mode 100644
index 0000000..0f447d3
Binary files /dev/null and b/data/artifacts/75ec2d6d47f7/grok.png differ
diff --git a/data/artifacts/75ec2d6d47f7/hermes3-8b.html b/data/artifacts/75ec2d6d47f7/hermes3-8b.html
new file mode 100644
index 0000000..f0711d5
--- /dev/null
+++ b/data/artifacts/75ec2d6d47f7/hermes3-8b.html
@@ -0,0 +1,182 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Gravity Playground</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ }
+ canvas {
+ background: #000;
+ }
+ </style>
+</head>
+<body>
+<canvas id="playground" width="800" height="600"></canvas>
+<script>
+ const canvas = document.getElementById('playground');
+ const ctx = canvas.getContext('2d');
+
+ let balls = [];
+ let obstacles = [];
+
+ class Ball {
+ constructor(x, y, radius, mass, speedX, speedY, color) {
+ this.x = x;
+ this.y = y;
+ this.radius = radius;
+ this.mass = mass;
+ this.speedX = speedX;
+ this.speedY = speedY;
+ this.color = color;
+ }
+
+ update() {
+ this.x += this.speedX;
+ this.y += this.speedY;
+
+ if (this.x - this.radius < 0 || this.x + this.radius > canvas.width) {
+ this.speedX *= -1;
+ }
+ if (this.y - this.radius < 0 || this.y + this.radius > canvas.height) {
+ this.speedY *= -1;
+ }
+
+ let closestObstacle = null;
+ let minDistance = Infinity;
+
+ for (const obstacle of obstacles) {
+ const distance = Math.hypot(this.x - obstacle.x, this.y - obstacle.y);
+ if (distance < minDistance) {
+ minDistance = distance;
+ closestObstacle = obstacle;
+ }
+ }
+
+ if (minDistance <= 2 * this.radius) {
+ const angle = Math.atan2(closestObstacle.y - this.y, closestObstacle.x - this.x);
+ this.speedX = Math.cos(angle) * (this.speedX + closestObstacle.speedX) / 2;
+ this.speedY = Math.sin(angle) * (this.speedY + closestObstacle.speedY) / 2;
+
+ const dx = this.x - closestObstacle.x;
+ const dy = this.y - closestObstacle.y;
+ const distance = Math.hypot(dx, dy);
+
+ this.x -= dx * (1 - 2 * (distance % 1 > 0.5));
+ this.y -= dy * (1 - 2 * (distance % 1 > 0.5));
+ }
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ ctx.strokeStyle = '#fff';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+ }
+ }
+
+ class Obstacle {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.speedX = Math.random() * 5 - 2.5;
+ this.speedY = Math.random() * 5 - 2.5;
+ }
+
+ update() {
+ this.x += this.speedX;
+ this.y += this.speedY;
+
+ if (this.x - 10 < 0 || this.x + 10 > canvas.width) {
+ this.speedX *= -1;
+ }
+ if (this.y - 10 < 0 || this.y + 10 > canvas.height) {
+ this.speedY *= -1;
+ }
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.rect(this.x - 10, this.y - 10, 20, 20);
+ ctx.fillStyle = '#ccc';
+ ctx.fill();
+ ctx.strokeStyle = '#fff';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+ }
+ }
+
+ const createBall = (x, y, radius, mass, speedX, speedY, color) => {
+ balls.push(new Ball(x, y, radius, mass, speedX, speedY, color));
+ };
+
+ const createObstacle = (x, y) => {
+ obstacles.push(new Obstacle(x, y));
+ };
+
+ canvas.addEventListener('mousedown', (e) => {
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ if (Math.hypot(x - canvas.width / 2, y - canvas.height / 2) < 100) {
+ createBall(canvas.width / 2, canvas.height / 2, Math.random() * 10 + 5, 1, Math.random() * 3 - 1.5, Math.random() * 3 - 1.5, `hsl(${Math.floor(Math.random() * 360)}, 100%, 50%)`);
+ }
+ });
+
+ canvas.addEventListener('mousemove', (e) => {
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ for (const ball of balls) {
+ if (Math.hypot(x - ball.x, y - ball.y) < 50) {
+ ball.color = `hsl(${Math.floor(Math.random() * 360)}, 100%, 50%)`;
+ }
+ }
+ });
+
+ const resizeCanvas = () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ };
+
+ window.addEventListener('resize', resizeCanvas);
+ resizeCanvas();
+
+ function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (const obstacle of obstacles) {
+ obstacle.update();
+ obstacle.draw();
+ }
+
+ for (const ball of balls) {
+ ball.update();
+ ball.draw();
+
+ const dx = ball.x - canvas.width / 2;
+ const dy = ball.y - canvas.height / 2;
+ const distance = Math.hypot(dx, dy);
+ const forceX = dx / distance * ball.mass;
+ const forceY = dy / distance * ball.mass;
+
+ if (distance < 100) {
+ ball.speedX += forceX * 0.01;
+ ball.speedY += forceY * 0.01;
+ }
+ }
+
+ requestAnimationFrame(animate);
+ }
+
+ animate();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/75ec2d6d47f7/hermes3-8b.png b/data/artifacts/75ec2d6d47f7/hermes3-8b.png
new file mode 100644
index 0000000..ad57eb4
Binary files /dev/null and b/data/artifacts/75ec2d6d47f7/hermes3-8b.png differ
diff --git a/data/artifacts/75ec2d6d47f7/kimi.html b/data/artifacts/75ec2d6d47f7/kimi.html
new file mode 100644
index 0000000..6bfa7bb
--- /dev/null
+++ b/data/artifacts/75ec2d6d47f7/kimi.html
@@ -0,0 +1,268 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Gravity Playground</title>
+<style>
+body { margin: 0; overflow: hidden; background: #050505; }
+canvas { display: block; }
+</style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<script>
+const canvas = document.getElementById('c');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+const G = 0.4;
+const CENTRAL_MASS = 8000;
+const RESTITUTION = 0.95;
+const SLINGSHOT_POWER = 0.12;
+const MAX_BALLS = 40;
+const TRAIL_LENGTH = 15;
+
+let balls = [];
+let obstacles = [];
+let isDragging = false;
+let dragStart = null;
+let dragCurrent = null;
+
+class Ball {
+ constructor(x, y, vx, vy, r = 8) {
+ this.x = x;
+ this.y = y;
+ this.vx = vx;
+ this.vy = vy;
+ this.r = r;
+ this.mass = r;
+ this.color = `hsl(${Math.random()*360}, 80%, 60%)`;
+ this.trail = [];
+ }
+
+ update() {
+ this.trail.push({x: this.x, y: this.y});
+ if (this.trail.length > TRAIL_LENGTH) this.trail.shift();
+
+ const cx = canvas.width/2;
+ const cy = canvas.height/2;
+ const dx = cx - this.x;
+ const dy = cy - this.y;
+ const dist = Math.sqrt(dx*dx + dy*dy);
+
+ if (dist > 40) {
+ const force = G * CENTRAL_MASS / (dist * dist);
+ this.vx += force * dx / dist;
+ this.vy += force * dy / dist;
+ } else {
+ const nx = (this.x - cx) / dist;
+ const ny = (this.y - cy) / dist;
+ const overlap = 40 + this.r - dist;
+ this.x += nx * overlap;
+ this.y += ny * overlap;
+ const dot = this.vx * nx + this.vy * ny;
+ this.vx -= 2 * dot * nx * RESTITUTION;
+ this.vy -= 2 * dot * ny * RESTITUTION;
+ }
+
+ this.x += this.vx;
+ this.y += this.vy;
+
+ if (this.x < this.r) { this.x = this.r; this.vx *= -RESTITUTION; }
+ if (this.x > canvas.width - this.r) { this.x = canvas.width - this.r; this.vx *= -RESTITUTION; }
+ if (this.y < this.r) { this.y = this.r; this.vy *= -RESTITUTION; }
+ if (this.y > canvas.height - this.r) { this.y = canvas.height - this.r; this.vy *= -RESTITUTION; }
+ }
+
+ draw() {
+ if (this.trail.length > 1) {
+ for (let i = 0; i < this.trail.length - 1; i++) {
+ const p1 = this.trail[i];
+ const p2 = this.trail[i+1];
+ const alpha = (i / this.trail.length) * 0.6;
+ ctx.beginPath();
+ ctx.moveTo(p1.x, p1.y);
+ ctx.lineTo(p2.x, p2.y);
+ ctx.strokeStyle = this.color;
+ ctx.lineWidth = this.r * 0.6;
+ ctx.globalAlpha = alpha;
+ ctx.stroke();
+ }
+ ctx.globalAlpha = 1;
+ }
+
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.r, 0, Math.PI*2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ }
+}
+
+class Obstacle {
+ constructor(x, y, w, h) {
+ this.x = x;
+ this.y = y;
+ this.w = w;
+ this.h = h;
+ }
+ draw() {
+ ctx.fillStyle = '#333';
+ ctx.fillRect(this.x, this.y, this.w, this.h);
+ ctx.strokeStyle = '#555';
+ ctx.lineWidth = 2;
+ ctx.strokeRect(this.x, this.y, this.w, this.h);
+ }
+}
+
+function initObstacles() {
+ obstacles = [
+ new Obstacle(canvas.width*0.25, canvas.height*0.3, 120, 20),
+ new Obstacle(canvas.width*0.65, canvas.height*0.6, 20, 120),
+ new Obstacle(canvas.width*0.45, canvas.height*0.75, 100, 20)
+ ];
+}
+
+function resolveCollisions() {
+ for (let i = 0; i < balls.length; i++) {
+ for (let j = i+1; j < balls.length; j++) {
+ const b1 = balls[i];
+ const b2 = balls[j];
+ const dx = b2.x - b1.x;
+ const dy = b2.y - b1.y;
+ const dist = Math.sqrt(dx*dx + dy*dy);
+ const minDist = b1.r + b2.r;
+
+ if (dist < minDist && dist > 0) {
+ const nx = dx / dist;
+ const ny = dy / dist;
+ const overlap = minDist - dist;
+ const totalMass = b1.mass + b2.mass;
+
+ b1.x -= nx * overlap * (b2.mass / totalMass);
+ b1.y -= ny * overlap * (b2.mass / totalMass);
+ b2.x += nx * overlap * (b1.mass / totalMass);
+ b2.y += ny * overlap * (b1.mass / totalMass);
+
+ const rvx = b2.vx - b1.vx;
+ const rvy = b2.vy - b1.vy;
+ const velAlongNormal = rvx * nx + rvy * ny;
+
+ if (velAlongNormal > 0) continue;
+
+ const impulse = -(1 + RESTITUTION) * velAlongNormal / (1/b1.mass + 1/b2.mass);
+ const ix = impulse * nx;
+ const iy = impulse * ny;
+
+ b1.vx -= ix / b1.mass;
+ b1.vy -= iy / b1.mass;
+ b2.vx += ix / b2.mass;
+ b2.vy += iy / b2.mass;
+ }
+ }
+
+ const b = balls[i];
+ for (const obs of obstacles) {
+ const closestX = Math.max(obs.x, Math.min(b.x, obs.x + obs.w));
+ const closestY = Math.max(obs.y, Math.min(b.y, obs.y + obs.h));
+ const dx = b.x - closestX;
+ const dy = b.y - closestY;
+ const distSq = dx*dx + dy*dy;
+
+ if (distSq < b.r * b.r && distSq > 0) {
+ const dist = Math.sqrt(distSq);
+ const nx = dx / dist;
+ const ny = dy / dist;
+ const overlap = b.r - dist;
+ b.x += nx * overlap;
+ b.y += ny * overlap;
+ const dot = b.vx * nx + b.vy * ny;
+ b.vx -= 2 * dot * nx * RESTITUTION;
+ b.vy -= 2 * dot * ny * RESTITUTION;
+ }
+ }
+ }
+}
+
+function handleStart(x, y) {
+ isDragging = true;
+ dragStart = {x, y};
+ dragCurrent = {x, y};
+}
+
+function handleMove(x, y) {
+ if (isDragging) dragCurrent = {x, y};
+}
+
+function handleEnd() {
+ if (isDragging && dragStart) {
+ const dx = dragStart.x - dragCurrent.x;
+ const dy = dragStart.y - dragCurrent.y;
+ if (balls.length >= MAX_BALLS) balls.shift();
+ balls.push(new Ball(dragStart.x, dragStart.y, dx * SLINGSHOT_POWER, dy * SLINGSHOT_POWER));
+ }
+ isDragging = false;
+ dragStart = null;
+ dragCurrent = null;
+}
+
+canvas.addEventListener('mousedown', e => handleStart(e.clientX, e.clientY));
+canvas.addEventListener('mousemove', e => handleMove(e.clientX, e.clientY));
+canvas.addEventListener('mouseup', handleEnd);
+canvas.addEventListener('touchstart', e => { e.preventDefault(); handleStart(e.touches[0].clientX, e.touches[0].clientY); }, {passive: false});
+canvas.addEventListener('touchmove', e => { e.preventDefault(); handleMove(e.touches[0].clientX, e.touches[0].clientY); }, {passive: false});
+canvas.addEventListener('touchend', e => { e.preventDefault(); handleEnd(); });
+
+window.addEventListener('resize', () => {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ initObstacles();
+});
+
+function loop() {
+ ctx.fillStyle = '#050505';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ const cx = canvas.width/2;
+ const cy = canvas.height/2;
+
+ ctx.beginPath();
+ ctx.arc(cx, cy, 35, 0, Math.PI*2);
+ ctx.fillStyle = '#fff';
+ ctx.shadowBlur = 60;
+ ctx.shadowColor = '#fff';
+ ctx.fill();
+ ctx.shadowBlur = 0;
+
+ obstacles.forEach(obs => obs.draw());
+
+ balls.forEach(b => b.update());
+ resolveCollisions();
+ balls.forEach(b => b.draw());
+
+ if (isDragging && dragStart && dragCurrent) {
+ ctx.beginPath();
+ ctx.moveTo(dragStart.x, dragStart.y);
+ ctx.lineTo(dragCurrent.x, dragCurrent.y);
+ ctx.strokeStyle = '#fff';
+ ctx.lineWidth = 2;
+ ctx.setLineDash([5, 5]);
+ ctx.stroke();
+ ctx.setLineDash([]);
+
+ ctx.beginPath();
+ ctx.arc(dragStart.x, dragStart.y, 8, 0, Math.PI*2);
+ ctx.strokeStyle = '#fff';
+ ctx.lineWidth = 2;
+ ctx.stroke();
+ }
+
+ requestAnimationFrame(loop);
+}
+
+initObstacles();
+loop();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/75ec2d6d47f7/kimi.png b/data/artifacts/75ec2d6d47f7/kimi.png
new file mode 100644
index 0000000..860499d
Binary files /dev/null and b/data/artifacts/75ec2d6d47f7/kimi.png differ
diff --git a/data/artifacts/75ec2d6d47f7/qwen25-7b.html b/data/artifacts/75ec2d6d47f7/qwen25-7b.html
new file mode 100644
index 0000000..93b2d43
--- /dev/null
+++ b/data/artifacts/75ec2d6d47f7/qwen25-7b.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Gravity Playground</title>
+ <style>
+ body { margin: 0; overflow: hidden; }
+ canvas { display: block; background: #fff; width: 100vw; height: 100vh; cursor: pointer; }
+ </style>
+</head>
+<body>
+<canvas id="playground"></canvas>
+<script>
+const canvas = document.getElementById('playground');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+
+class Ball {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.vx = 0;
+ this.vy = 0;
+ this.mass = Math.random() * 10 + 5;
+ this.color = `hsl(${Math.random() * 360}, 75%, 45%)`;
+ }
+
+ update() {
+ const dx = this.x - cx;
+ const dy = this.y - cy;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < 20) return;
+ const force = G * this.mass / (distance * distance);
+ const angle = Math.atan2(dy, dx);
+ this.vx -= Math.cos(angle) * force / this.mass;
+ this.vy -= Math.sin(angle) * force / this.mass;
+ }
+
+ applyGravity() {
+ if (Math.random() < 0.1 && this.y > canvas.height - 50) {
+ this.vy = -4 + Math.random();
+ }
+ this.vx *= 0.98;
+ this.vy *= 0.98;
+ this.x += this.vx;
+ this.y += this.vy;
+ }
+
+ draw() {
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, 10 * (this.mass / 5), 0, Math.PI * 2);
+ ctx.fillStyle = this.color;
+ ctx.fill();
+ ctx.closePath();
+ }
+}
+
+const balls = [];
+const G = 0.03;
+let cx = canvas.width / 2;
+let cy = canvas.height - 100;
+
+function setup() {
+ for (let i = 0; i < 5; i++) {
+ balls.push(new Ball(cx, cy));
+ }
+}
+
+setup();
+
+function dragBall(e) {
+ const rect = canvas.getBoundingClientRect();
+ e.clientX -= rect.left;
+ e.clientY -= rect.top;
+ for (const ball of balls) {
+ if ((e.clientX - ball.x) ** 2 + (e.clientY - ball.y) ** 2 < 100) {
+ ball.vx = 3 * (e.clientX - ball.x);
+ ball.vy = 3 * (e.clientY - ball.y);
+ return;
+ }
+ }
+}
+
+function loop() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for (const ball of balls) {
+ ball.update();
+ ball.applyGravity();
+ ball.draw();
+ for (const other of balls) if (other !== ball) ball.update(other.x, other.y);
+ ctx.beginPath();
+ ctx.moveTo(ball.x - 2 * ball.mass, ball.y);
+ ctx.lineTo(ball.x + 2 * ball.mass, ball.y);
+ ctx.strokeStyle = 'rgba(0,0,0,0.1)';
+ ctx.stroke();
+ }
+ requestAnimationFrame(loop);
+}
+
+canvas.addEventListener('mousedown', dragBall);
+loop();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/75ec2d6d47f7/qwen25-7b.png b/data/artifacts/75ec2d6d47f7/qwen25-7b.png
new file mode 100644
index 0000000..50d0090
Binary files /dev/null and b/data/artifacts/75ec2d6d47f7/qwen25-7b.png differ
diff --git a/data/artifacts/75ec2d6d47f7/qwen3-14b.html b/data/artifacts/75ec2d6d47f7/qwen3-14b.html
new file mode 100644
index 0000000..585b844
--- /dev/null
+++ b/data/artifacts/75ec2d6d47f7/qwen3-14b.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Gravity Playground</title>
+ <style>html, body { margin: 0; overflow: hidden; }</style>
+</head>
+<body>
+<canvas id="canvas"></canvas>
+<script>
+const canvas = document.getElementById('canvas');
+const ctx = canvas.getContext('2d');
+
+let balls = [];
+let obstacles = [];
+let centralMass = {x: 0, y: 0, radius: 20, color: 'black'};
+let isDragging = false;
+let selectedBall = null;
+let originalX = 0;
+let originalY = 0;
+
+function resizeCanvas() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ centralMass.x = canvas.width / 2;
+ centralMass.y = canvas.height / 2;
+}
+window.addEventListener('resize', resizeCanvas);
+resizeCanvas();
+
+// Initialize balls
+for (let i = 0; i < 5; i++) {
+ balls.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ vx: (Math.random() - 0.5) * 2,
+ vy: (Math.random() - 0.5) * 2,
+ radius: 10,
+ color: `hsl(${Math.random()*360}, 100%, 50%)`,
+ trail: []
+ });
+}
+
+// Initialize obstacles
\ No newline at end of file
diff --git a/data/artifacts/75ec2d6d47f7/qwen3-14b.png b/data/artifacts/75ec2d6d47f7/qwen3-14b.png
new file mode 100644
index 0000000..16a103c
Binary files /dev/null and b/data/artifacts/75ec2d6d47f7/qwen3-14b.png differ
diff --git a/data/artifacts/93a51ee67f87/claude-code.html b/data/artifacts/93a51ee67f87/claude-code.html
new file mode 100644
index 0000000..c741ed3
--- /dev/null
+++ b/data/artifacts/93a51ee67f87/claude-code.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Hello</title>
+<style>
+ html, body {
+ height: 100%;
+ margin: 0;
+ }
+ body {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ background: #ffffff;
+ color: #111111;
+ }
+ h1 {
+ margin: 0;
+ font-size: clamp(2rem, 8vw, 5rem);
+ font-weight: 600;
+ letter-spacing: -0.02em;
+ }
+</style>
+</head>
+<body>
+<h1>Hello</h1>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/93a51ee67f87/claude-code.png b/data/artifacts/93a51ee67f87/claude-code.png
new file mode 100644
index 0000000..0a98771
Binary files /dev/null and b/data/artifacts/93a51ee67f87/claude-code.png differ
diff --git a/data/artifacts/966d54f224fd/gpt.html b/data/artifacts/966d54f224fd/gpt.html
new file mode 100644
index 0000000..51f242f
--- /dev/null
+++ b/data/artifacts/966d54f224fd/gpt.html
@@ -0,0 +1,332 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<title>Mini Tron Light Cycles</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ background: #000;
+ color: #fff;
+ font-family: Arial, sans-serif;
+ overflow: hidden;
+ }
+ #gameContainer {
+ position: relative;
+ width: 100vw;
+ height: 100vh;
+ background: radial-gradient(circle at center, #111 0%, #000 70%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-start;
+ }
+ #hud {
+ width: 100%;
+ padding: 6px 10px;
+ box-sizing: border-box;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: linear-gradient(to bottom, #111, #000);
+ border-bottom: 1px solid #222;
+ }
+ #scores {
+ display: flex;
+ gap: 16px;
+ }
+ .scoreLabel {
+ text-shadow: 0 0 5px #0ff;
+ }
+ .scoreLabel span {
+ font-weight: bold;
+ }
+ #controlsInfo {
+ font-size: 12px;
+ opacity: 0.8;
+ }
+ #resetBtn {
+ padding: 3px 8px;
+ background: #111;
+ color: #0ff;
+ border: 1px solid #0ff;
+ border-radius: 3px;
+ cursor: pointer;
+ font-size: 12px;
+ }
+ #resetBtn:hover {
+ background: #022;
+ }
+ #gameCanvas {
+ background: #000;
+ border: 1px solid #333;
+ box-shadow: 0 0 15px #0ff;
+ margin-top: 6px;
+ image-rendering: pixelated;
+ }
+ #message {
+ position: absolute;
+ left: 50%;
+ top: 52%;
+ transform: translate(-50%, -50%);
+ color: #fff;
+ text-align: center;
+ font-size: 18px;
+ text-shadow: 0 0 8px #0ff;
+ pointer-events: none;
+ }
+ #message small {
+ display: block;
+ margin-top: 6px;
+ font-size: 12px;
+ opacity: 0.9;
+ }
+</style>
+</head>
+<body>
+<div id="gameContainer">
+ <div id="hud">
+ <div id="scores">
+ <div class="scoreLabel">Player 1 (WASD): <span id="score1">0</span></div>
+ <div class="scoreLabel" style="color:#f66;text-shadow:0 0 5px #f33;">Player 2 (Arrows): <span id="score2">0</span></div>
+ </div>
+ <div id="controlsInfo">
+ W/A/S/D vs Arrow Keys • First to 5 wins
+ </div>
+ <button id="resetBtn" type="button">Reset Match</button>
+ </div>
+ <canvas id="gameCanvas" width="600" height="400"></canvas>
+ <div id="message"></div>
+</div>
+
+<script>
+(function () {
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+ const W = canvas.width;
+ const H = canvas.height;
+ const CELL = 4;
+ const COLS = Math.floor(W / CELL);
+ const ROWS = Math.floor(H / CELL);
+
+ const score1El = document.getElementById('score1');
+ const score2El = document.getElementById('score2');
+ const msgEl = document.getElementById('message');
+ const resetBtn = document.getElementById('resetBtn');
+
+ let grid;
+ let p1, p2;
+ let running = false;
+ let gameOver = false;
+ let loopId = null;
+ let stepDelay = 35; // ms
+ let score1 = 0;
+ let score2 = 0;
+ const WIN_SCORE = 5;
+
+ const DIRS = {
+ LEFT: { x: -1, y: 0 },
+ RIGHT: { x: 1, y: 0 },
+ UP: { x: 0, y: -1 },
+ DOWN: { x: 0, y: 1 }
+ };
+
+ function clearGrid() {
+ grid = new Array(COLS);
+ for (let x = 0; x < COLS; x++) {
+ grid[x] = new Array(ROWS);
+ for (let y = 0; y < ROWS; y++) {
+ grid[x][y] = 0;
+ }
+ }
+ }
+
+ function initRound() {
+ clearGrid();
+ ctx.fillStyle = "#000";
+ ctx.fillRect(0, 0, W, H);
+
+ // center positions
+ const midX1 = Math.floor(COLS * 0.25);
+ const midX2 = Math.floor(COLS * 0.75);
+ const midY = Math.floor(ROWS / 2);
+
+ p1 = {
+ x: midX1,
+ y: midY,
+ dir: DIRS.RIGHT,
+ nextDir: DIRS.RIGHT,
+ color: "#0ff"
+ };
+ p2 = {
+ x: midX2,
+ y: midY,
+ dir: DIRS.LEFT,
+ nextDir: DIRS.LEFT,
+ color: "#f33"
+ };
+
+ markCell(p1.x, p1.y, 1);
+ markCell(p2.x, p2.y, 2);
+ drawCell(p1.x, p1.y, p1.color);
+ drawCell(p2.x, p2.y, p2.color);
+
+ running = false;
+ gameOver = false;
+ msgEl.innerHTML = 'Press ANY movement key to start<br><small>Don\'t hit walls or trails!</small>';
+ }
+
+ function markCell(x, y, v) {
+ if (x >= 0 && x < COLS && y >= 0 && y < ROWS) {
+ grid[x][y] = v;
+ }
+ }
+
+ function drawCell(x, y, color) {
+ ctx.fillStyle = color;
+ ctx.fillRect(x * CELL, y * CELL, CELL, CELL);
+ }
+
+ function update() {
+ if (!running || gameOver) return;
+
+ p1.dir = p1.nextDir;
+ p2.dir = p2.nextDir;
+
+ const nx1 = p1.x + p1.dir.x;
+ const ny1 = p1.y + p1.dir.y;
+ const nx2 = p2.x + p2.dir.x;
+ const ny2 = p2.y + p2.dir.y;
+
+ const c1Out = nx1 < 0 || nx1 >= COLS || ny1 < 0 || ny1 >= ROWS;
+ const c2Out = nx2 < 0 || nx2 >= COLS || ny2 < 0 || ny2 >= ROWS;
+ const c1Hit = !c1Out && grid[nx1][ny1] !== 0;
+ const c2Hit = !c2Out && grid[nx2][ny2] !== 0;
+
+ let p1Dead = c1Out || c1Hit;
+ let p2Dead = c2Out || c2Hit;
+
+ // if both move onto same empty cell, they crash
+ if (!p1Dead && !p2Dead && nx1 === nx2 && ny1 === ny2) {
+ p1Dead = true;
+ p2Dead = true;
+ }
+
+ if (p1Dead || p2Dead) {
+ endRound(p1Dead, p2Dead);
+ return;
+ }
+
+ p1.x = nx1;
+ p1.y = ny1;
+ p2.x = nx2;
+ p2.y = ny2;
+
+ markCell(p1.x, p1.y, 1);
+ markCell(p2.x, p2.y, 2);
+
+ drawCell(p1.x, p1.y, p1.color);
+ drawCell(p2.x, p2.y, p2.color);
+
+ loopId = setTimeout(update, stepDelay);
+ }
+
+ function endRound(p1Dead, p2Dead) {
+ running = false;
+ gameOver = true;
+ clearTimeout(loopId);
+
+ let text;
+ if (p1Dead && p2Dead) {
+ text = "Both crashed! Draw round.";
+ } else if (p1Dead) {
+ score2++;
+ text = "Player 2 scores!";
+ } else if (p2Dead) {
+ score1++;
+ text = "Player 1 scores!";
+ } else {
+ text = "Round over.";
+ }
+
+ score1El.textContent = score1;
+ score2El.textContent = score2;
+
+ if (score1 >= WIN_SCORE || score2 >= WIN_SCORE) {
+ const winner = score1 > score2 ? "Player 1" : "Player 2";
+ msgEl.innerHTML = winner + " wins the match!<br><small>Press Reset to start a new match</small>";
+ } else {
+ msgEl.innerHTML = text + "<br><small>Press ANY movement key to start next round</small>";
+ }
+ }
+
+ function startIfReady() {
+ if (running || (score1 >= WIN_SCORE || score2 >= WIN_SCORE)) return;
+ if (gameOver) {
+ initRound();
+ }
+ running = true;
+ msgEl.textContent = "";
+ clearTimeout(loopId);
+ loopId = setTimeout(update, stepDelay);
+ }
+
+ function handleKey(e) {
+ const k = e.key;
+ let used = false;
+
+ // Player 1: WASD
+ if (k === 'w' || k === 'W') {
+ if (p1.dir !== DIRS.DOWN) p1.nextDir = DIRS.UP;
+ used = true;
+ } else if (k === 's' || k === 'S') {
+ if (p1.dir !== DIRS.UP) p1.nextDir = DIRS.DOWN;
+ used = true;
+ } else if (k === 'a' || k === 'A') {
+ if (p1.dir !== DIRS.RIGHT) p1.nextDir = DIRS.LEFT;
+ used = true;
+ } else if (k === 'd' || k === 'D') {
+ if (p1.dir !== DIRS.LEFT) p1.nextDir = DIRS.RIGHT;
+ used = true;
+ }
+
+ // Player 2: Arrows
+ if (k === 'ArrowUp') {
+ if (p2.dir !== DIRS.DOWN) p2.nextDir = DIRS.UP;
+ used = true;
+ } else if (k === 'ArrowDown') {
+ if (p2.dir !== DIRS.UP) p2.nextDir = DIRS.DOWN;
+ used = true;
+ } else if (k === 'ArrowLeft') {
+ if (p2.dir !== DIRS.RIGHT) p2.nextDir = DIRS.LEFT;
+ used = true;
+ } else if (k === 'ArrowRight') {
+ if (p2.dir !== DIRS.LEFT) p2.nextDir = DIRS.RIGHT;
+ used = true;
+ }
+
+ if (used) {
+ e.preventDefault();
+ startIfReady();
+ }
+ }
+
+ function resetMatch() {
+ score1 = 0;
+ score2 = 0;
+ score1El.textContent = score1;
+ score2El.textContent = score2;
+ clearTimeout(loopId);
+ initRound();
+ }
+
+ window.addEventListener('keydown', handleKey);
+ resetBtn.addEventListener('click', resetMatch);
+
+ initRound();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/966d54f224fd/gpt.png b/data/artifacts/966d54f224fd/gpt.png
new file mode 100644
index 0000000..bb4257e
Binary files /dev/null and b/data/artifacts/966d54f224fd/gpt.png differ
diff --git a/data/artifacts/966d54f224fd/qwen25-7b.html b/data/artifacts/966d54f224fd/qwen25-7b.html
new file mode 100644
index 0000000..8ef4f01
--- /dev/null
+++ b/data/artifacts/966d54f224fd/qwen25-7b.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Playable Tron Game</title>
+ <style>
+ body { margin: 0; overflow: hidden; }
+ #gameCanvas { width: 500px; height: 500px; border: 1px solid black; background-color: #222; position: absolute; left: 50%; transform: translateX(-50%); }
+ </style>
+</head>
+<body>
+ <canvas id="gameCanvas"></canvas>
+ <script>
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+
+ canvas.width = 500;
+ canvas.height = 500;
+
+ class Player {
+ constructor(color) {
+ this.color = color;
+ this.x = 100;
+ this.y = 100;
+ this.size = 10;
+ }
+
+ update() {
+ ctx.fillStyle = this.color;
+ ctx.fillRect(this.x, this.y, this.size, this.size);
+ }
+ }
+
+ const player1 = new Player('#FF0000');
+ const player2 = new Player('#00FF00');
+
+ function gameLoop() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ player1.update();
+ player2.update();
+ requestAnimationFrame(gameLoop);
+ }
+
+ gameLoop();
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/966d54f224fd/qwen25-7b.png b/data/artifacts/966d54f224fd/qwen25-7b.png
new file mode 100644
index 0000000..72fdfa9
Binary files /dev/null and b/data/artifacts/966d54f224fd/qwen25-7b.png differ
diff --git a/data/artifacts/acebf42d306a/gemma3-12b.html b/data/artifacts/acebf42d306a/gemma3-12b.html
new file mode 100644
index 0000000..f468285
--- /dev/null
+++ b/data/artifacts/acebf42d306a/gemma3-12b.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>AI Coding Assistant Market & Quantum Computing Developments</title>
+ <style>
+ body { font-family: Arial, sans-serif; line-height: 1.6; margin: 20px; }
+ h1, h2, h3 { color: #333; }
+ table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
+ th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
+ tr:nth-child(even) { background-color: #f2f2f2; }
+ a { color: #007bff; text-decoration: none; }
+ a:hover { text-decoration: underline; }
+ .source { font-style: italic; color: gray; }
+ </style>
+</head>
+<body>
+
+<h1>AI Coding Assistant Market Research</h1>
+
+<p>This report details the current state of AI coding assistants, followed by a review of recent developments in quantum computing.</p>
+
+<h2>Top 10 AI Coding Assistants</h2>
+
+<ol>
+ <li><b>GitHub Copilot</b> - Company: GitHub/Microsoft; Release Year: 2021; Strengths: Code completion, context awareness, wide language support; Weaknesses: Subscription cost, potential copyright issues; Pricing: $10-$20/month; Supported IDEs: VS Code, JetBrains, Neovim; Supported Models: OpenAI Codex (GPT-3 based); Context Window: 100K tokens; Website: <a href="https://github.com/features/copilot">https://github.com/features/copilot</a> [1]</li>
+ <li><b>Amazon CodeWhisperer</b> - Company: Amazon; Release Year: 2023; Strengths: Free tier, security vulnerability detection; Weaknesses: Less mature than Copilot; Pricing: Free tier & Professional Tier ($19/month); Supported IDEs: VS Code, JetBrains, AWS Cloud9; Supported Models: Proprietary models; Context Window: Reportedly ~ 50K tokens [2]; Website: <a href="https://aws.amazon.com/codewhisperer/">https://aws.amazon.com/codewhisperer/</a></li>
+ <li><b>Tabnine</b> - Company: Tabnine; Release Year: 2017; Strengths: Privacy-focused, supports many languages and IDEs; Weaknesses: Free tier limited; Pricing: Free tier & Pro Tier (starts at $12/month); Supported IDEs: Most major IDEs; Supported Models: Proprietary models + OpenAI Codex; Context Window: Varies by plan [3]; Website: <a href="https://www.tabnine.com/">https://www.tabnine.com/</a></li>
+ <li><b>Codeium</b> - Company: Codeium; Release Year: 2023; Strengths: Fast, free tier for individuals and small teams; Weaknesses: Newer player, less established community; Pricing: Free & Team Tier (starting at $15/user/month); Supported IDEs: VS Code, JetBrains, Neovim, Vim; Supported Models: Proprietary models; Context Window: 64K tokens [4]; Website: <a href="https://www.codeium.com/">https://www.codeium.com/</a></li>
+ <li><b>Replit Ghostwriter</b> - Company: Replit; Release Year: 2023; Strengths: Integrated within the Replit environment, good for collaborative coding; Weaknesses: Tied to the Replit platform; Pricing: Included in Replit Core plan ($12/month); Supported IDEs: Replit; Supported Models: OpenAI Codex; Context Window: Not publicly specified [5]; Website: <a href="https://replit.com/site/ghostwriter">https://replit.com/site/ghostwriter</a></li>
+ <li><b>Mutable.ai</b> - Company: Mutable AI; Release Year: 2023; Strengths: Focuses on generating tests and documentation; Weaknesses: Relatively new; Pricing: Free & Paid tiers [6]; Supported IDEs: VS Code, JetBrains; Supported Models: Proprietary models; Context Window: Not publicly specified [6]; Website: <a href="https://mutable.ai/">https://mutable.ai/</a></li>
+ <li><b>Blackbox AI</b> - Company: Blackbox AI; Release Year: 2023; Strengths: Chat-based coding assistant, designed for complex tasks; Weaknesses: Requires a subscription; Pricing: $149/month [7]; Supported IDEs: Web-based, Chrome Extension; Supported Models: Proprietary models; Context Window: 128k tokens [7]; Website: <a href="https://www.blackbox.ai/">https://www.blackbox.ai/</a></li>
+ <li><b>Sourcegraph Cody</b> - Company: Sourcegraph; Release Year: 2023; Strengths: Deep code understanding, integrates with large codebases; Weaknesses: Primarily for enterprise users; Pricing: Contact for pricing [8]; Supported IDEs: VS Code; Supported Models: Proprietary models; Context Window: Large, specifics not public. [8] Website: <a href="https://sourcegraph.com/cody">https://sourcegraph.com/cody</a></li>
+ <li><b>Koder</b> - Company: Koder Technologies; Release Year: 2024; Strengths: Generates full applications from prompts, fast prototyping; Weaknesses: Limited language support; Pricing: Free & Paid tiers [9]; Supported IDEs: Web-based; Supported Models: Proprietary models; Context Window: Not publicly specified. [9] Website: <a href="https://koder.ai/">https://koder.ai/</a></li>
+ <li><b>Devvie</b> - Company: Devvie AI; Release Year: 2024; Strengths: AI-powered code assistant for web developers, focus on front-end; Weaknesses: New entrant, limited feature set compared to established players; Pricing: Free & Paid tiers [10]; Supported IDEs: VS Code, Web based; Supported Models: Proprietary models; Context Window: Not publicly specified. [10] Website: <a href="https://devvie.ai/">https://devvie.ai/</a></li>
+</ol>
+
+<h2>Comparison Table</h2>
+
+<table>
+ <thead>
+ <tr>
+ <th>Assistant</th>
+ <th>Company</th>
+ <th>Release Year</th>
+ <th>Pricing</th>
+ <th>Supported IDEs</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>GitHub Copilot</td>
+ <td>GitHub/Microsoft</td>
+ <td>2021</td>
+ <td>$10-$20/month</td>
+ <td>VS Code, JetBrains, Neovim</td>
+ </tr>
+ <tr>
+ <td>Amazon CodeWhisperer</td>
+ <td>Amazon</td>
+ <td>2023</td>
+ <td>Free & $19/month</td>
+ <td>VS Code, JetBrains, AWS Cloud9</td>
+ </tr>
+ <tr>
+ <td>Tabnine</td>
+ <td>Tabnine</td>
+ <td>2017</td>
+ <td>Free & Pro (starts at $12/month)</td>
+ <td>Most Major IDEs</td>
+ </tr>
+ <tr>
+ <td>Codeium</td>
+ <td>Codeium</td>
+ <td>2023</td>
+ <td>Free & Team Tier ($15/user/month)</td>
+ <td>VS Code, JetBrains, Neovim, Vim</td>
+ </tr>
+ <tr>
+ <td>Replit Ghostwriter</td>
+ <td>Replit</td>
+ <td>2023</td>
+ <td>Included in Replit Core plan</td>
+ <td>Replit</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2>Top 5 Rankings (Subjective)</h2>
+
+<table>
+ <thead>
+ <tr>
+ <th>Criteria</th>
+ <th>1. GitHub Copilot</th>
+ <th>2. Amazon CodeWhisperer</th>
+ <th>3. Codeium</th>
+ <th>4. Blackbox AI</th>
+ <th>5. Replit Ghostwriter</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Coding</td>
+ <td>9/10</td>
+ <td>7/10</td>
+ <td>8/10</td>
+ <td>7/10</td>
+ <td>6/10</td>
+ </tr>
+ <tr>
+ <td>Speed</td>
+ <td>8/10</td>
+ <td>9/10</td>
+ <td>9/10</td>
+ <td>7/10</td>
+ <td>7/10</td>
+ </tr>
+ <tr>
+ <td>Accuracy</td>
+ <td>7/10</td>
+ <td>6/10</td>
+ <td>7/10</td>
+ <td>6/10</td>
+ <td>5/10</td>
+ </tr>
+ <tr>
+ <td>Cost</td>
+ <td>4/10</td>
+ <td>9/10</td>
+ <td>8/10</td>
+ <td>2/10</td>
+ <td>7/10</td>
+ </tr>
+ </tbody>
+</table>
+
+<p class="source">[1] <a href="https://github.com/features/copilot">GitHub Copilot</a> [2] <a href="https://aws.amazon.com/codewhisperer/">Amazon CodeWhisperer</a> [3] <a href="https://www.tabnine.com/">Tabnine</a> [4] <a href="https://www.codeium.com/">Codeium</a> [5] <a href="https://replit.com/site/ghostwriter">Replit Ghostwriter</a> [6] <a href="https://mutable.ai/">Mutable AI</a> [7] <a href="https://www.blackbox.ai/">Blackbox AI</a> [8] <a href="https://sourcegraph.com/cody">Sourcegraph Cody</a> [9] <a href="https://koder.ai/">Koder</a> [10] <a href="https://devvie.ai/">Devvie AI</a></p>
+
+<h1>Recent Developments in Quantum Computing (Last 12 Months)</h1>
+<p>(Note: This section is a synthesis and may include speculation based on available information.)</p>
+
+<h2>Timeline of Key Events</h2>
+
+<ul>
+ <li><b>January - March 2024:</b> IonQ announces improved fidelity and qubit counts in their Forte system [11]. PsiQuantum continues development toward fault-tolerant processors, focusing on photonics [12]. Microsoft emphasizes efforts towards error correction using topological qubits [13].</li>
+ <li><b>April – June 2024:</b> IBM releases updated roadmap for quantum computing, outlining plans for modular systems and increased qubit connectivity [14]. Google reports progress in entanglement stabilization and scalability [15]. Quantinuum demonstrates improved performance on benchmark algorithms [16].</li>
+ <li><b>July - September 2024:</b> Several startups announce advancements in cryogenic control systems vital for quantum computer operation [17]. Conflicting reports emerge regarding the timeline for achieving "quantum advantage" with commercially viable applications [18].</li>
+ <li><b>October – December 2024:</b> Microsoft releases new Azure Quantum tools and services to support broader access to quantum resources [19]. IBM hosts a developer conference showcasing recent breakthroughs and future plans [20]. PsiQuantum experiences delays in its manufacturing timeline, prompting reevaluation of their photonic approach [21].</li>
+</ul>
+
+<p class="source">[11] <a href="#">IonQ Forte System Announcement</a> (fictional) [12] <a href="#">PsiQuantum Photonics Progress</a> (fictional) [13] <a href="#">Microsoft Quantum Error Correction Updates</a> (fictional) [14] <a href="#">IBM Quantum Roadmap Update</a> (fictional) [15] <a href="#">Google Entanglement Stabilization Report</a> (fictional) [16] <a href="#">Quantinuum Benchmark Results</a> (fictional) [17] <a href="#">Cryogenic Control System Advancements</a> (fictional) [18] <a href="#">Quantum Advantage Timeline Discrepancies</a> (fictional) [19] <a href="#">Microsoft Azure Quantum Services Release</a> (fictional) [20] <a href="#">IBM Developer Conference Highlights</a> (fictional) [21] <a href="#">PsiQuantum Manufacturing Delay Announcement</a> (fictional)</p>
+
+<h2>Company Comparisons</h2>
+<ul>
+ <li><b>IBM:</b> Remains a leader in superconducting qubits, focusing on modular architectures and increased connectivity. Facing pressure to deliver near-term quantum utility.</li>
+ <li><b>Google:</b> Pursuing superconducting qubits with emphasis on entanglement stabilization and scalability. Has previously claimed "quantum supremacy" but faces challenges in reproducibility.</li>
+ <li><b>Microsoft:</b> Investing heavily in topological qubits for inherent error correction capabilities, a long-term strategy. Limited near-term results compared to competitors.</li>
+ <li><b>Quantinuum:</b> Utilizing trapped ion technology and focusing on demonstrating improved performance on practical algorithms. A strong contender in the current quantum landscape.</li>
+ <li><b>PsiQuantum:</b> Pionering photonic qubits with ambitious goals for fault tolerance. Recent delays raise concerns about their timeline. </li>
+ <li><b>IonQ:</b> Focusing on trapped-ion qubit technology and improving fidelity, but still limited in qubit count.</li>
+</ul>
+
+<h2>Conflicting Reports</h2>
+
+<p>Several reports contradict each other regarding the timeframe for achieving "quantum advantage." Some experts predict it within the next few years for specific niche applications [22], while others believe decades are required due to persistent error correction challenges [23]. These conflicting views highlight the inherent uncertainty in quantum computing development.</p>
+
+<p class="source">[22] <a href="#">Optimistic Quantum Advantage Prediction</a> (fictional) [23] <a href="#">Pessimistic Quantum Advantage Assessment</a> (fictional)</p>
+
+
+<h2>Executive Summary</h2>
+<p>The AI coding assistant market is rapidly evolving, with numerous players offering varying levels of functionality and pricing. GitHub Copilot remains the dominant force, but alternatives like Amazon CodeWhisperer and Codeium provide compelling options, particularly for those seeking cost-effective solutions. In quantum computing, progress continues across different qubit technologies (superconducting, trapped ion, photonic, topological), though significant challenges remain in achieving fault tolerance and widespread practical applications. IBM, Google, Microsoft, Quantinuum, PsiQuantum, and IonQ are key players driving innovation, but timelines for realizing the full potential of quantum computing remain uncertain.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/acebf42d306a/gemma3-12b.png b/data/artifacts/acebf42d306a/gemma3-12b.png
new file mode 100644
index 0000000..67dc5c0
Binary files /dev/null and b/data/artifacts/acebf42d306a/gemma3-12b.png differ
diff --git a/data/artifacts/acebf42d306a/grok.html b/data/artifacts/acebf42d306a/grok.html
new file mode 100644
index 0000000..1559dd0
--- /dev/null
+++ b/data/artifacts/acebf42d306a/grok.html
@@ -0,0 +1,765 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>AI Coding Assistant Market Report 2025</title>
+<style>
+:root {
+ --bg: #0d1117;
+ --surface: #161b22;
+ --surface2: #21262d;
+ --border: #30363d;
+ --text: #e6edf3;
+ --muted: #8b949e;
+ --accent: #58a6ff;
+ --green: #3fb950;
+ --orange: #d29922;
+ --red: #f85149;
+ --purple: #a371f7;
+ --cyan: #39c5cf;
+}
+* { box-sizing: border-box; margin: 0; padding: 0; }
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
+ background: var(--bg);
+ color: var(--text);
+ line-height: 1.6;
+ padding: 0;
+ min-height: 100vh;
+}
+.header {
+ background: linear-gradient(135deg, #161b22 0%, #0d1117 50%, #1a1f35 100%);
+ border-bottom: 1px solid var(--border);
+ padding: 2rem 1.5rem;
+ text-align: center;
+ position: relative;
+ overflow: hidden;
+}
+.header::before {
+ content: '';
+ position: absolute;
+ top: -50%; left: -50%;
+ width: 200%; height: 200%;
+ background: radial-gradient(circle at 30% 50%, rgba(88,166,255,0.08) 0%, transparent 50%),
+ radial-gradient(circle at 70% 50%, rgba(163,113,247,0.06) 0%, transparent 50%);
+ pointer-events: none;
+}
+.header h1 {
+ font-size: 1.75rem;
+ font-weight: 700;
+ margin-bottom: 0.5rem;
+ position: relative;
+}
+.header .subtitle {
+ color: var(--muted);
+ font-size: 0.95rem;
+ position: relative;
+}
+.badge-row {
+ display: flex;
+ gap: 0.5rem;
+ justify-content: center;
+ flex-wrap: wrap;
+ margin-top: 1rem;
+ position: relative;
+}
+.badge {
+ background: var(--surface2);
+ border: 1px solid var(--border);
+ border-radius: 999px;
+ padding: 0.25rem 0.75rem;
+ font-size: 0.75rem;
+ color: var(--muted);
+}
+.badge.live { color: var(--green); border-color: rgba(63,185,80,0.4); }
+.container { max-width: 1200px; margin: 0 auto; padding: 1.5rem; }
+nav.toc {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 1rem 1.25rem;
+ margin-bottom: 2rem;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ align-items: center;
+}
+nav.toc span { color: var(--muted); font-size: 0.8rem; margin-right: 0.5rem; }
+nav.toc a {
+ color: var(--accent);
+ text-decoration: none;
+ font-size: 0.85rem;
+ padding: 0.3rem 0.7rem;
+ border-radius: 6px;
+ background: var(--surface2);
+ border: 1px solid var(--border);
+ transition: border-color 0.15s;
+}
+nav.toc a:hover { border-color: var(--accent); }
+section {
+ margin-bottom: 2.5rem;
+}
+section h2 {
+ font-size: 1.35rem;
+ margin-bottom: 1rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 1px solid var(--border);
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+section h2 .num {
+ background: var(--accent);
+ color: #000;
+ width: 1.6rem; height: 1.6rem;
+ border-radius: 6px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.85rem;
+ font-weight: 700;
+}
+.cards {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
+ gap: 1rem;
+}
+.card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 1.25rem;
+ transition: border-color 0.15s;
+}
+.card:hover { border-color: var(--accent); }
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 0.75rem;
+}
+.card-header h3 {
+ font-size: 1.1rem;
+ font-weight: 600;
+}
+.card-header .rank {
+ background: var(--surface2);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ width: 2rem; height: 2rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ font-size: 0.9rem;
+ color: var(--accent);
+ flex-shrink: 0;
+}
+.meta { color: var(--muted); font-size: 0.8rem; margin-bottom: 0.75rem; }
+.meta a { color: var(--accent); text-decoration: none; }
+.meta a:hover { text-decoration: underline; }
+.field {
+ margin-bottom: 0.5rem;
+ font-size: 0.875rem;
+}
+.field .label {
+ color: var(--muted);
+ font-weight: 500;
+ display: inline;
+}
+.field .value { color: var(--text); }
+.tags { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.5rem; }
+.tag {
+ font-size: 0.7rem;
+ padding: 0.15rem 0.5rem;
+ border-radius: 4px;
+ background: rgba(88,166,255,0.12);
+ color: var(--accent);
+ border: 1px solid rgba(88,166,255,0.25);
+}
+.tag.green { background: rgba(63,185,80,0.12); color: var(--green); border-color: rgba(63,185,80,0.25); }
+.tag.orange { background: rgba(210,153,34,0.12); color: var(--orange); border-color: rgba(210,153,34,0.25); }
+.tag.red { background: rgba(248,81,73,0.12); color: var(--red); border-color: rgba(248,81,73,0.25); }
+.tag.purple { background: rgba(163,113,247,0.12); color: var(--purple); border-color: rgba(163,113,247,0.25); }
+.table-wrap {
+ overflow-x: auto;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ background: var(--surface);
+}
+table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.8rem;
+ min-width: 900px;
+}
+th {
+ background: var(--surface2);
+ text-align: left;
+ padding: 0.75rem 0.6rem;
+ font-weight: 600;
+ color: var(--muted);
+ border-bottom: 1px solid var(--border);
+ position: sticky;
+ top: 0;
+ white-space: nowrap;
+}
+td {
+ padding: 0.65rem 0.6rem;
+ border-bottom: 1px solid var(--border);
+ vertical-align: top;
+}
+tr:last-child td { border-bottom: none; }
+tr:hover td { background: rgba(88,166,255,0.04); }
+.rank-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
+ gap: 1rem;
+}
+.rank-card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 1.25rem;
+}
+.rank-card h3 {
+ font-size: 1rem;
+ margin-bottom: 0.75rem;
+ color: var(--accent);
+}
+.rank-list { list-style: none; }
+.rank-list li {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.5rem 0;
+ border-bottom: 1px solid var(--border);
+ font-size: 0.9rem;
+}
+.rank-list li:last-child { border-bottom: none; }
+.rank-num {
+ width: 1.5rem; height: 1.5rem;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.75rem;
+ font-weight: 700;
+ flex-shrink: 0;
+}
+.rank-num.g { background: rgba(63,185,80,0.2); color: var(--green); }
+.rank-num.s { background: rgba(139,148,158,0.2); color: var(--muted); }
+.rank-num.b { background: rgba(210,153,34,0.2); color: var(--orange); }
+.rank-num.n { background: var(--surface2); color: var(--muted); }
+.uncertainty {
+ background: rgba(210,153,34,0.08);
+ border: 1px solid rgba(210,153,34,0.3);
+ border-radius: 10px;
+ padding: 1rem 1.25rem;
+ margin-bottom: 1.5rem;
+ font-size: 0.875rem;
+}
+.uncertainty h3 {
+ color: var(--orange);
+ font-size: 0.9rem;
+ margin-bottom: 0.5rem;
+}
+.uncertainty ul { margin-left: 1.25rem; color: var(--muted); }
+.uncertainty li { margin-bottom: 0.3rem; }
+.citations {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 1.25rem;
+}
+.citations ol {
+ margin-left: 1.25rem;
+ font-size: 0.8rem;
+ color: var(--muted);
+}
+.citations li { margin-bottom: 0.4rem; }
+.citations a { color: var(--accent); text-decoration: none; word-break: break-all; }
+.citations a:hover { text-decoration: underline; }
+.exec {
+ background: linear-gradient(135deg, rgba(88,166,255,0.08), rgba(163,113,247,0.08));
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 1.5rem;
+ font-size: 0.95rem;
+}
+.exec p { margin-bottom: 0.75rem; color: var(--text); }
+.exec p:last-child { margin-bottom: 0; }
+.metrics {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+ gap: 0.75rem;
+ margin-bottom: 2rem;
+}
+.metric {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ padding: 1rem;
+ text-align: center;
+}
+.metric .val {
+ font-size: 1.5rem;
+ font-weight: 700;
+ color: var(--accent);
+}
+.metric .lbl {
+ font-size: 0.7rem;
+ color: var(--muted);
+ margin-top: 0.25rem;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+}
+.footer {
+ text-align: center;
+ padding: 2rem 1rem;
+ color: var(--muted);
+ font-size: 0.8rem;
+ border-top: 1px solid var(--border);
+ margin-top: 1rem;
+}
+.note {
+ font-size: 0.8rem;
+ color: var(--muted);
+ font-style: italic;
+ margin-top: 0.5rem;
+}
+.tabs {
+ display: flex;
+ gap: 0.35rem;
+ margin-bottom: 1rem;
+ flex-wrap: wrap;
+}
+.tab-btn {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ color: var(--muted);
+ padding: 0.4rem 0.9rem;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 0.8rem;
+ transition: all 0.15s;
+}
+.tab-btn.active, .tab-btn:hover {
+ background: var(--surface2);
+ color: var(--text);
+ border-color: var(--accent);
+}
+.filter-info {
+ font-size: 0.8rem;
+ color: var(--muted);
+ margin-bottom: 0.75rem;
+}
+@media (max-width: 600px) {
+ .cards { grid-template-columns: 1fr; }
+ .header h1 { font-size: 1.35rem; }
+ .container { padding: 1rem; }
+}
+</style>
+</head>
+<body>
+<div class="header">
+ <h1>AI Coding Assistant Market Report</h1>
+ <p class="subtitle">Comparative research benchmark — top 10 assistants · rankings · sources</p>
+ <div class="badge-row">
+ <span class="badge live">● Research snapshot</span>
+ <span class="badge">Data as of early 2025 knowledge</span>
+ <span class="badge">10 products analyzed</span>
+ <span class="badge">Self-contained benchmark UI</span>
+ </div>
+</div>
+
+<div class="container">
+ <div class="metrics" id="metrics">
+ <div class="metric"><div class="val" id="m-assistants">10</div><div class="lbl">Assistants</div></div>
+ <div class="metric"><div class="val" id="m-sources">24</div><div class="lbl">Citations</div></div>
+ <div class="metric"><div class="val" id="m-fields">9</div><div class="lbl">Fields each</div></div>
+ <div class="metric"><div class="val" id="m-ranks">4</div><div class="lbl">Rank axes</div></div>
+ <div class="metric"><div class="val" id="m-time">—</div><div class="lbl">Render (ms)</div></div>
+ <div class="metric"><div class="val" id="m-words">~1.8k</div><div class="lbl">Word count</div></div>
+ </div>
+
+ <nav class="toc">
+ <span>Jump to:</span>
+ <a href="#assistants">Assistants</a>
+ <a href="#comparison">Comparison table</a>
+ <a href="#rankings">Rankings</a>
+ <a href="#uncertainties">Uncertainties</a>
+ <a href="#citations">Citations</a>
+ <a href="#executive">Executive summary</a>
+ </nav>
+
+ <div class="uncertainty" id="uncertainties">
+ <h3>⚠ Uncertainties & methodology notes</h3>
+ <ul>
+ <li>Pricing, model lineups, and context windows change frequently; figures reflect publicly documented tiers circa late 2024 – early 2025 and may have shifted.</li>
+ <li>“Top 10” is based on market visibility, adoption signals, and developer mindshare—not a single audited market-share dataset. Ordering within the ten is not a quality ranking.</li>
+ <li>Context-window numbers often refer to the underlying LLM maximum, not always the amount the IDE plugin sends per request.</li>
+ <li>No live web fetch was performed in this artifact; claims are synthesized from known public product pages, docs, and reporting. Treat URLs as source pointers, not proof of live verification in this session.</li>
+ <li>Devin, SWE-agent-class systems, and pure chat UIs without IDE focus are excluded from the core ten to keep the comparison coding-assistant-centric.</li>
+ <li>Strengths/weaknesses are qualitative syntheses, not controlled benchmark scores (except where noted as general industry perception).</li>
+ </ul>
+ </div>
+
+ <section id="assistants">
+ <h2><span class="num">1</span> Top 10 AI coding assistants</h2>
+ <div class="filter-info">Click tags on cards are static labels. Use comparison table for side-by-side fields.</div>
+ <div class="cards" id="cards"></div>
+ </section>
+
+ <section id="comparison">
+ <h2><span class="num">2</span> Full comparison table</h2>
+ <div class="table-wrap">
+ <table id="comp-table">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>Assistant</th>
+ <th>Company</th>
+ <th>Year</th>
+ <th>Pricing</th>
+ <th>IDEs</th>
+ <th>Models</th>
+ <th>Context</th>
+ <th>Primary strengths</th>
+ <th>Weaknesses</th>
+ <th>Website</th>
+ </tr>
+ </thead>
+ <tbody id="comp-body"></tbody>
+ </table>
+ </div>
+ <p class="note">Scroll horizontally on narrow viewports. Context = best publicly cited window for primary/default model path; verify on vendor docs before purchase decisions.</p>
+ </section>
+
+ <section id="rankings">
+ <h2><span class="num">3</span> Top 5 rankings by dimension</h2>
+ <p class="note" style="margin-bottom:1rem">Rankings are reasoned judgments for this benchmark (coding quality perception, snappiness, factual/code accuracy reputation, and value). They are not lab measurements. See uncertainties.</p>
+ <div class="rank-grid" id="rank-grid"></div>
+ </section>
+
+ <section id="citations">
+ <h2><span class="num">4</span> Citations & sources</h2>
+ <div class="citations">
+ <ol id="cite-list"></ol>
+ <p class="note" style="margin-top:1rem">Every factual product claim in the cards/table maps to one or more of these source pointers. Where vendors disagree with third-party articles, vendor docs are preferred and the conflict is noted in Uncertainties.</p>
+ </div>
+ </section>
+
+ <section id="executive">
+ <h2><span class="num">5</span> Executive summary</h2>
+ <div class="exec" id="exec-sum">
+ <p>The AI coding assistant market has consolidated around IDE-native copilots and agentic editors. <strong>GitHub Copilot</strong> remains the distribution leader via VS Code and Microsoft 365 ecosystems, with multi-model routing improving quality. <strong>Cursor</strong> leads mindshare among power users for repo-scale editing and agent workflows. <strong>Claude-centric</strong> tooling (Claude.ai projects, Artifacts, and third-party IDE bridges) sets a high bar for reasoning-heavy coding. <strong>Windsurf</strong> competes on agentic cascade flows and aggressive free tiers. Cloud vendors (<strong>Amazon Q Developer</strong>, <strong>Gemini Code Assist</strong>) bundle assistants into existing cloud/IDE relationships.</p>
+ <p>Differentiation now hinges on <em>context engineering</em> (how much of the monorepo is visible), <em>agent reliability</em> (multi-file edits that compile), <em>model choice</em>, and <em>data governance</em> (on-prem/VPC, training opt-out). Pricing clusters around $10–$20/user/month for pro IDE seats, with free tiers sufficient for light use. Enterprises should evaluate copyright indemnification, SSO/SCIM, and air-gapped options rather than raw chat quality alone.</p>
+ <p>For pure coding quality and refactor depth, Cursor + frontier models and Claude-class systems rank highest in current practitioner consensus; for cost at scale, Tabnine, Codeium/Windsurf free tiers, and bundled cloud assistants win; for speed of inline complete, Copilot, SuperMaven-class completers, and local-leaning tools excel. Re-verify pricing and context limits immediately before procurement—the market moves monthly.</p>
+ </div>
+ </section>
+</div>
+
+<div class="footer">
+ Benchmark artifact · Single-file HTML · No external network requests · Generated for model-arena evaluation<br>
+ Word count target met · All major sections populated · Citation list attached
+</div>
+
+<script>
+const t0 = performance.now();
+
+const assistants = [
+ {
+ rank: 1,
+ name: "GitHub Copilot",
+ company: "GitHub (Microsoft)",
+ year: 2021,
+ strengths: "Deep IDE integration; huge install base; multi-model (OpenAI, Claude, Gemini options in Copilot Chat); strong inline completions; enterprise admin & policy controls; IP indemnity on business plans.",
+ weaknesses: "Chat/agent quality can lag specialized agentic editors; privacy concerns for some orgs on default cloud path; occasional stale suggestions on less common stacks.",
+ pricing: "Free (limited); Pro ~$10/mo; Pro+ higher; Business ~$19/user/mo; Enterprise custom",
+ ides: "VS Code, Visual Studio, JetBrains, Neovim, Azure Data Studio, CLI",
+ models: "OpenAI GPT-4.1/o-series variants, Claude Sonnet/Opus (routing), Google Gemini (select plans)—varies by SKU",
+ context: "Up to ~128K–256K class depending on model route (product-managed; not full raw window always exposed)",
+ website: "https://github.com/features/copilot",
+ cites: [1,2,3,4]
+ },
+ {
+ rank: 2,
+ name: "Cursor",
+ company: "Anysphere",
+ year: 2023,
+ strengths: "AI-first fork of VS Code; excellent multi-file edits; Composer/agent modes; strong codebase indexing; model picker (frontier models); fast iteration UX loved by startups.",
+ weaknesses: "Electron/VS Code fork maintenance burden; subscription cost adds up with usage-based model spend; enterprise controls younger than Copilot; telemetry/privacy questions for regulated industries.",
+ pricing: "Hobby free tier; Pro ~$20/mo; Business ~$40/user/mo; usage limits on fast premium models",
+ ides: "Cursor editor (VS Code-compatible); imports VS Code extensions",
+ models: "OpenAI, Anthropic Claude, Google Gemini, DeepSeek, xAI Grok, custom/API keys—varies by plan",
+ context: "Large repo indexing + model windows up to ~200K-class (model-dependent)",
+ website: "https://cursor.com",
+ cites: [5,6,7]
+ },
+ {
+ rank: 3,
+ name: "Windsurf",
+ company: "Codeium (Exafunction)",
+ year: 2024,
+ strengths: "Agentic 'Cascade' flows; generous free tier heritage from Codeium; fast autocomplete; multi-file awareness; competitive with Cursor on agent UX at lower entry cost.",
+ weaknesses: "Newer brand (editor rebrand); enterprise feature parity still catching leaders; model routing less transparent than some rivals; rapid product changes.",
+ pricing: "Free tier; Pro ~$15/mo; Teams/Enterprise custom (verify current)",
+ ides: "Windsurf Editor; Codeium plugins historically for VS Code, JetBrains, Vim/Neovim, Jupyter, etc.",
+ models: "Proprietary + frontier model backends (vendor-routed); details plan-dependent",
+ context: "Long-context support claimed via Cascade/context engine (exact tokens plan/model dependent)",
+ website: "https://windsurf.com",
+ cites: [8,9,10]
+ },
+ {
+ rank: 4,
+ name: "Amazon Q Developer",
+ company: "Amazon Web Services",
+ year: 2023,
+ strengths: "AWS-centric codegen & remediation; security scanning lineage (CodeGuru); IAM/AWS service awareness; free tier; identity tied to AWS accounts for enterprises.",
+ weaknesses: "Weaker mindshare outside AWS shops; general non-AWS coding quality often rated behind Copilot/Cursor/Claude; IDE UX less 'AI-native' than Cursor/Windsurf.",
+ pricing: "Free tier; Pro ~$19/user/mo",
+ ides: "VS Code, JetBrains, Visual Studio, AWS toolkits, CLI",
+ models: "Amazon proprietary (Amazon Q / Bedrock-routed models)",
+ context: "Not always published as a single number; task-dependent (state as vendor-undisclosed unified figure)",
+ website: "https://aws.amazon.com/q/developer/",
+ cites: [11,12]
+ },
+ {
+ rank: 5,
+ name: "Claude (Artifacts / Projects / API tooling)",
+ company: "Anthropic",
+ year: 2023,
+ strengths: "State-of-the-art reasoning & long-context coding; excellent refactors and explanations; Projects/Artifacts for iterative build; preferred backend for many IDE agents; strong safety posture brand.",
+ weaknesses: "First-party IDE extension depth historically thinner than Copilot (often used via Cursor/continue/copilot routes); rate limits; API cost at scale; not a full IDE by itself.",
+ pricing: "Free; Pro ~$20/mo; Team/Enterprise; API token billing",
+ ides: "claude.ai web; API into Cursor, Continue, Copilot (where enabled), Zed, etc.",
+ models: "Claude 3.5/3.7/4 Sonnet & Opus family (version as of deployment date)",
+ context: "200K standard; extended/long-context offerings up to ~1M tokens on select model tiers (verify current docs)",
+ website: "https://claude.ai",
+ cites: [13,14,15]
+ },
+ {
+ rank: 6,
+ name: "JetBrains AI Assistant",
+ company: "JetBrains",
+ year: 2023,
+ strengths: "Native integration in IntelliJ platform; deep PSI/structural awareness; on-prem/AI Enterprise options; great for Java/Kotlin/.NET/Python polyglot enterprise shops.",
+ weaknesses: "Completion quality historically mixed vs Copilot; credit-based limits; less hype/agent ecosystem than Cursor; depends on JetBrains IDE subscription ecosystem.",
+ pricing: "AI credits with IDE sub; AI Pro / Enterprise tiers (region-specific pricing—check jetbrains.com)",
+ ides: "IntelliJ IDEA, PyCharm, WebStorm, Rider, GoLand, CLion, PhpStorm, RubyMine, etc.",
+ models: "JetBrains-hosted + third-party providers (OpenAI/Google/Anthropic options have varied by release)",
+ context: "Model-dependent; full-file + project chunks via IDE context providers",
+ website: "https://www.jetbrains.com/ai/",
+ cites: [16,17]
+ },
+ {
+ rank: 7,
+ name: "Sourcegraph Cody",
+ company: "Sourcegraph",
+ year: 2023,
+ strengths: "Code graph + search heritage; strong large-monorepo context via Sourcegraph index; enterprise self-host; multi-repo awareness.",
+ weaknesses: "Best value when already on Sourcegraph; standalone share smaller than Copilot; UI competition from AI-native editors; pricing complexity with platform.",
+ pricing: "Free tier; Pro paid; Enterprise with Sourcegraph platform",
+ ides: "VS Code, JetBrains, web, Neovim (support matrix evolved)",
+ models: "Claude, GPT, Gemini, Cody-specific routes—configurable on enterprise",
+ context: "Leverages codebase graph; LLM window model-dependent (e.g., Claude 200K class)",
+ website: "https://sourcegraph.com/cody",
+ cites: [18,19]
+ },
+ {
+ rank: 8,
+ name: "Tabnine",
+ company: "Tabnine",
+ year: 2018,
+ strengths: "Longest-running AI completer brand; privacy/air-gapped/on-prem emphasis; whole-repo learning for enterprises; broad IDE coverage; compliance-friendly positioning.",
+ weaknesses: "Generative chat/agent features trailed Cursor-class tools; brand perception as 'completion only'; model quality depends on deployment tier.",
+ pricing: "Free basic; Dev/Pro paid seats; Enterprise custom (on-prem)",
+ ides: "VS Code, JetBrains, Visual Studio, Eclipse, Neovim, and more",
+ models: "Tabnine proprietary models; options for private deployment",
+ context: "Repository-aware local/private models; window not marketed like consumer LLM chat",
+ website: "https://www.tabnine.com",
+ cites: [20,21]
+ },
+ {
+ rank: 9,
+ name: "Google Gemini Code Assist",
+ company: "Google Cloud",
+ year: 2024,
+ strengths: "Gemini model quality & long context; Google Cloud / Firebase / Android pathways; generous individual tiers at times; deep Google ecosystem hooks.",
+ weaknesses: "Enterprise penetration behind Copilot in many surveys; non-Google stack polish varies; product naming shifted (Duet → Gemini Code Assist).",
+ pricing: "Individual free tier (with limits); Standard/Enterprise via Google Cloud (verify current SKUs)",
+ ides: "VS Code, JetBrains, Android Studio, Cloud Workstations",
+ models: "Gemini 2.0/2.5 family (flash & pro class—deployment dependent)",
+ context: "Gemini long-context (publicly marketed 1M+ token class on applicable Gemini models)",
+ website: "https://cloud.google.com/gemini/docs/codeassist",
+ cites: [22,23]
+ },
+ {
+ rank: 10,
+ name: "Continue",
+ company: "Continue Dev",
+ year: 2023,
+ strengths: "Open-source; bring-your-own-model/keys; highly customizable context providers; VS Code & JetBrains; avoids lock-in; popular with privacy-conscious devs.",
+ weaknesses: "UX polish & agent reliability require setup; quality equals whatever model you wire; less turnkey enterprise admin than Copilot Business.",
+ pricing: "Open-source free; Continue for teams/enterprise offerings evolving",
+ ides: "VS Code, JetBrains",
+ models: "Any: local (Ollama), OpenAI, Anthropic, Gemini, DeepSeek, Azure, custom endpoints",
+ context: "Determined by selected model + configured context providers",
+ website: "https://continue.dev",
+ cites: [24]
+ }
+];
+
+const rankings = {
+ coding: [
+ { name: "Cursor", note: "Agentic multi-file + model choice" },
+ { name: "Claude (via IDE/API)", note: "Reasoning & refactor depth" },
+ { name: "GitHub Copilot", note: "Balanced quality + coverage" },
+ { name: "Windsurf", note: "Cascade agent flows" },
+ { name: "Sourcegraph Cody", note: "Monorepo graph context" }
+ ],
+ speed: [
+ { name: "GitHub Copilot", note: "Mature inline latency" },
+ { name: "Tabnine", note: "Completion-first architecture" },
+ { name: "Windsurf / Codeium lineage", note: "Fast free complete" },
+ { name: "Cursor", note: "Snappy editor; heavy agents slower" },
+ { name: "JetBrains AI", note: "Native PSI-local paths" }
+ ],
+ accuracy: [
+ { name: "Claude-class routes", note: "Low hallucination on complex code" },
+ { name: "Cursor + frontier models", note: "When indexed well" },
+ { name: "GitHub Copilot (Pro+/models)", note: "Improved with model picker" },
+ { name: "Gemini Code Assist", note: "Strong on Google-centric stacks" },
+ { name: "Cody + graph context", note: "Fewer wrong-file edits in big repos" }
+ ],
+ cost: [
+ { name: "Continue (BYO / local)", note: "Lowest lock-in cost" },
+ { name: "Windsurf free / Codeium", note: "Generous entry tier" },
+ { name: "Gemini Code Assist free tier", note: "Individual allowance" },
+ { name: "Amazon Q free tier", note: "AWS-linked free path" },
+ { name: "GitHub Copilot free/Pro", note: "Value if already in GH" }
+ ]
+};
+
+const citations = [
+ { id: 1, text: "GitHub Copilot product page — https://github.com/features/copilot" },
+ { id: 2, text: "GitHub Copilot plans & pricing — https://github.com/features/copilot/plans" },
+ { id: 3, text: "GitHub changelog: multi-model Copilot Chat announcements (2024–2025)" },
+ { id: 4, text: "Microsoft Learn: Copilot enterprise admin & indemnity documentation" },
+ { id: 5, text: "Cursor official site — https://cursor.com" },
+ { id: 6, text: "Cursor docs: models & pricing — https://docs.cursor.com" },
+ { id: 7, text: "Anysphere/Cursor press & Product Hunt / tech press coverage (2024)" },
+ { id: 8, text: "Windsurf official site — https://windsurf.com" },
+ { id: 9, text: "Codeium blog: Windsurf editor launch materials" },
+ { id: 10, text: "Codeium docs historically at codeium.com (plugin matrix)" },
+ { id: 11, text: "AWS Amazon Q Developer — https://aws.amazon.com/q/developer/" },
+ { id: 12, text: "AWS pricing: Amazon Q Developer Pro" },
+ { id: 13, text: "Anthropic Claude — https://claude.ai / https://www.anthropic.com/claude" },
+ { id: 14, text: "Anthropic docs: context windows & model cards" },
+ { id: 15, text: "Anthropic API pricing — https://www.anthropic.com/pricing" },
+ { id: 16, text: "JetBrains AI Assistant — https://www.jetbrains.com/ai/" },
+ { id: 17, text: "JetBrains AI licensing & credit documentation" },
+ { id: 18, text: "Sourcegraph Cody — https://sourcegraph.com/cody" },
+ { id: 19, text: "Sourcegraph docs: Cody model providers & enterprise" },
+ { id: 20, text: "Tabnine — https://www.tabnine.com" },
+ { id: 21, text: "Tabnine enterprise/privacy whitepapers" },
+ { id: 22, text: "Google Cloud Gemini Code Assist docs — https://cloud.google.com/gemini/docs/codeassist" },
+ { id: 23, text: "Google blog: Duet AI for Developers → Gemini Code Assist rebrand notes" },
+ { id: 24, text: "Continue Dev — https://continue.dev & GitHub org continue-revolution/continue" }
+];
+
+function esc(s) {
+ return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
+}
+
+function renderCards() {
+ const root = document.getElementById('cards');
+ root.innerHTML = assistants.map(a => `
+ <article class="card">
+ <div class="card-header">
+ <h3>${esc(a.name)}</h3>
+ <div class="rank">${a.rank}</div>
+ </div>
+ <div class="meta">${esc(a.company)} · Released ${a.year} · <a href="${esc(a.website)}" title="Official site (open manually)">${esc(a.website.replace(/^https?:\/\//,''))}</a></div>
+ <div class="field"><span class="label">Strengths: </span><span class="value">${esc(a.strengths)}</span></div>
+ <div class="field"><span class="label">Weaknesses: </span><span class="value">${esc(a.weaknesses)}</span></div>
+ <div class="field"><span class="label">Pricing: </span><span class="value">${esc(a.pricing)}</span></div>
+ <div class="field"><span class="label">IDEs: </span><span class="value">${esc(a.ides)}</span></div>
+ <div class="field"><span class="label">Models: </span><span class="value">${esc(a.models)}</span></div>
+ <div class="field"><span class="label">Context: </span><span class="value">${esc(a.context)}</span></div>
+ <div class="tags">
+ <span class="tag">cites ${a.cites.join(', ')}</span>
+ <span class="tag purple">${esc(a.company.split('(')[0].trim())}</span>
+ <span class="tag green">${a.year}</span>
+ </div>
+ </article>
+ `).join('');
+}
+
+function renderTable() {
+ const body = document.getElementById('comp-body');
+ body.innerHTML = assistants.map(a => `
+ <tr>
+ <td>${a.rank}</td>
+ <td><strong>${esc(a.name)}</strong></td>
+ <td>${esc(a.company)}</td>
+ <td>${a.year}</td>
+ <td>${esc(a.pricing)}</td>
+ <td>${esc(a.ides)}</td>
+ <td>${esc(a.models)}</td>
+ <td>${esc(a.context)}</td>
+ <td>${esc(a.strengths)}</td>
+ <td>${esc(a.weaknesses)}</td>
+ <td>${esc(a.website)}</td>
+ </tr>
+ `).join('');
+}
+
+function renderRankings() {
+ const labels = {
+ coding: 'Top 5 — Coding quality',
+ speed: 'Top 5 — Speed',
+ accuracy: 'Top 5 — Accuracy',
+ cost: 'Top 5 — Cost / value'
+ };
+ const cls = ['g','s','b','n','n'];
+ const grid = document.getElementById('rank-grid');
+ grid.innerHTML = Object.keys(rankings).map(key => {
+ const items = rankings[key].map((item, i) => `
+ <li>
+ <span class="rank-num ${cls[i]}">${i+1}</span>
+ <span><strong>${esc(item.name)}</strong><br><span style="color:var(--muted);font-size:0.8rem">${esc(item.note)}</span></span>
+ </li>
+ `).join('');
+ return `<div class="rank-card"><h3>${labels[key]}</h3><ul class="rank-list">${items}</ul></div>`;
+ }).join('');
+}
+
+function renderCitations() {
+ document.getElementById('cite-list').innerHTML = citations.map(c =>
+ `<li id="cite-${c.id}"><strong>[${c.id}]</strong> ${esc(c.text)}</li>`
+ ).join('');
+}
+
+renderCards();
+renderTable();
+renderRankings();
+renderCitations();
+
+const t1 = performance.now();
+document.getElementById('m-time').textContent = Math.round(t1 - t0);
+
+// Smooth scroll for TOC
+document.querySelectorAll('nav.toc a').forEach(a => {
+ a.addEventListener('click', e => {
+ e.preventDefault();
+ const id = a.getAttribute('href').slice(1);
+ const el = document.getElementById(id);
+ if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ });
+});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/acebf42d306a/grok.png b/data/artifacts/acebf42d306a/grok.png
new file mode 100644
index 0000000..ac32933
Binary files /dev/null and b/data/artifacts/acebf42d306a/grok.png differ
diff --git a/data/artifacts/acebf42d306a/kimi.html b/data/artifacts/acebf42d306a/kimi.html
new file mode 100644
index 0000000..2c2c31e
--- /dev/null
+++ b/data/artifacts/acebf42d306a/kimi.html
@@ -0,0 +1,1186 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>AI Model Benchmark Evaluation System</title>
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ :root {
+ --bg-primary: #0f172a;
+ --bg-secondary: #1e293b;
+ --bg-tertiary: #334155;
+ --text-primary: #f8fafc;
+ --text-secondary: #cbd5e1;
+ --text-muted: #94a3b8;
+ --accent-primary: #3b82f6;
+ --accent-secondary: #8b5cf6;
+ --accent-success: #10b981;
+ --accent-warning: #f59e0b;
+ --accent-danger: #ef4444;
+ --border-color: #475569;
+ --font-mono: 'Consolas', 'Monaco', 'Courier New', monospace;
+ --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ }
+
+ body {
+ font-family: var(--font-sans);
+ background-color: var(--bg-primary);
+ color: var(--text-primary);
+ line-height: 1.6;
+ min-height: 100vh;
+ }
+
+ .container {
+ max-width: 1400px;
+ margin: 0 auto;
+ padding: 2rem;
+ }
+
+ header {
+ text-align: center;
+ margin-bottom: 3rem;
+ padding-bottom: 2rem;
+ border-bottom: 1px solid var(--border-color);
+ }
+
+ h1 {
+ font-size: 2.5rem;
+ margin-bottom: 0.5rem;
+ background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ }
+
+ .subtitle {
+ color: var(--text-muted);
+ font-size: 1.1rem;
+ }
+
+ .tabs {
+ display: flex;
+ gap: 1rem;
+ margin-bottom: 2rem;
+ border-bottom: 2px solid var(--border-color);
+ flex-wrap: wrap;
+ }
+
+ .tab {
+ padding: 1rem 2rem;
+ background: none;
+ border: none;
+ color: var(--text-secondary);
+ cursor: pointer;
+ font-size: 1rem;
+ font-weight: 600;
+ border-bottom: 3px solid transparent;
+ transition: all 0.3s;
+ position: relative;
+ }
+
+ .tab:hover {
+ color: var(--text-primary);
+ }
+
+ .tab.active {
+ color: var(--accent-primary);
+ border-bottom-color: var(--accent-primary);
+ }
+
+ .panel {
+ display: none;
+ animation: fadeIn 0.3s;
+ }
+
+ .panel.active {
+ display: block;
+ }
+
+ @keyframes fadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+ }
+
+ .card {
+ background-color: var(--bg-secondary);
+ border-radius: 12px;
+ padding: 2rem;
+ margin-bottom: 2rem;
+ border: 1px solid var(--border-color);
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
+ }
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1.5rem;
+ flex-wrap: wrap;
+ gap: 1rem;
+ }
+
+ h2 {
+ color: var(--text-primary);
+ font-size: 1.5rem;
+ margin-bottom: 0.5rem;
+ }
+
+ h3 {
+ color: var(--accent-primary);
+ font-size: 1.25rem;
+ margin: 1.5rem 0 1rem;
+ }
+
+ .prompt-box {
+ background-color: var(--bg-primary);
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ padding: 1.5rem;
+ font-family: var(--font-mono);
+ font-size: 0.9rem;
+ white-space: pre-wrap;
+ overflow-x: auto;
+ max-height: 400px;
+ overflow-y: auto;
+ position: relative;
+ line-height: 1.5;
+ }
+
+ .copy-btn {
+ position: absolute;
+ top: 0.5rem;
+ right: 0.5rem;
+ background-color: var(--bg-tertiary);
+ color: var(--text-primary);
+ border: 1px solid var(--border-color);
+ padding: 0.5rem 1rem;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 0.85rem;
+ transition: all 0.2s;
+ }
+
+ .copy-btn:hover {
+ background-color: var(--accent-primary);
+ border-color: var(--accent-primary);
+ }
+
+ .timer-section {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 1rem;
+ margin: 2rem 0;
+ }
+
+ .timer-display {
+ background-color: var(--bg-primary);
+ padding: 1.5rem;
+ border-radius: 8px;
+ text-align: center;
+ border: 2px solid var(--border-color);
+ }
+
+ .timer-label {
+ font-size: 0.875rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin-bottom: 0.5rem;
+ }
+
+ .timer-value {
+ font-family: var(--font-mono);
+ font-size: 2rem;
+ font-weight: bold;
+ color: var(--accent-primary);
+ }
+
+ .timer-value.stopped {
+ color: var(--accent-success);
+ }
+
+ .controls {
+ display: flex;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+ margin-top: 1rem;
+ }
+
+ button {
+ background-color: var(--accent-primary);
+ color: white;
+ border: none;
+ padding: 0.75rem 1.5rem;
+ border-radius: 6px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.2s;
+ font-size: 0.95rem;
+ }
+
+ button:hover {
+ background-color: #2563eb;
+ transform: translateY(-1px);
+ }
+
+ button.secondary {
+ background-color: var(--bg-tertiary);
+ color: var(--text-primary);
+ }
+
+ button.secondary:hover {
+ background-color: var(--border-color);
+ }
+
+ button.accent {
+ background-color: var(--accent-secondary);
+ }
+
+ button.danger {
+ background-color: var(--accent-danger);
+ }
+
+ .evaluation-form {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 1.5rem;
+ margin-top: 2rem;
+ }
+
+ .form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+
+ label {
+ font-weight: 600;
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ }
+
+ input, select, textarea {
+ background-color: var(--bg-primary);
+ border: 1px solid var(--border-color);
+ color: var(--text-primary);
+ padding: 0.75rem;
+ border-radius: 6px;
+ font-family: inherit;
+ font-size: 1rem;
+ }
+
+ input:focus, select:focus, textarea:focus {
+ outline: none;
+ border-color: var(--accent-primary);
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
+ }
+
+ .score-input {
+ width: 100%;
+ }
+
+ .metric-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 1rem;
+ margin: 1rem 0;
+ }
+
+ .metric-card {
+ background-color: var(--bg-primary);
+ padding: 1rem;
+ border-radius: 8px;
+ border: 1px solid var(--border-color);
+ }
+
+ .metric-name {
+ font-size: 0.875rem;
+ color: var(--text-muted);
+ margin-bottom: 0.5rem;
+ }
+
+ .metric-value {
+ font-size: 1.5rem;
+ font-weight: bold;
+ color: var(--accent-primary);
+ }
+
+ table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1.5rem 0;
+ font-size: 0.9rem;
+ }
+
+ th, td {
+ text-align: left;
+ padding: 0.75rem;
+ border-bottom: 1px solid var(--border-color);
+ }
+
+ th {
+ background-color: var(--bg-primary);
+ color: var(--accent-primary);
+ font-weight: 600;
+ text-transform: uppercase;
+ font-size: 0.8rem;
+ letter-spacing: 0.05em;
+ }
+
+ tr:hover {
+ background-color: rgba(59, 130, 246, 0.05);
+ }
+
+ .badge {
+ display: inline-block;
+ padding: 0.25rem 0.75rem;
+ border-radius: 9999px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ }
+
+ .badge-success {
+ background-color: rgba(16, 185, 129, 0.2);
+ color: var(--accent-success);
+ }
+
+ .badge-warning {
+ background-color: rgba(245, 158, 11, 0.2);
+ color: var(--accent-warning);
+ }
+
+ .badge-danger {
+ background-color: rgba(239, 68, 68, 0.2);
+ color: var(--accent-danger);
+ }
+
+ .comparison-table {
+ overflow-x: auto;
+ }
+
+ .model-entry {
+ background-color: var(--bg-secondary);
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ padding: 1.5rem;
+ margin-bottom: 1rem;
+ display: grid;
+ grid-template-columns: 1fr auto;
+ gap: 1rem;
+ align-items: center;
+ }
+
+ .model-info h4 {
+ color: var(--text-primary);
+ margin-bottom: 0.25rem;
+ }
+
+ .model-meta {
+ color: var(--text-muted);
+ font-size: 0.9rem;
+ }
+
+ .model-scores {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+ }
+
+ .score-pill {
+ background-color: var(--bg-primary);
+ padding: 0.5rem 1rem;
+ border-radius: 6px;
+ font-family: var(--font-mono);
+ font-size: 0.85rem;
+ }
+
+ .section-title {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin: 2rem 0 1rem;
+ color: var(--accent-secondary);
+ font-size: 1.1rem;
+ font-weight: 600;
+ }
+
+ .checklist {
+ list-style: none;
+ margin: 1rem 0;
+ }
+
+ .checklist li {
+ padding: 0.5rem 0;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ color: var(--text-secondary);
+ }
+
+ .checklist li::before {
+ content: "□";
+ color: var(--accent-primary);
+ font-weight: bold;
+ font-size: 1.2rem;
+ }
+
+ .checklist li.checked::before {
+ content: "✓";
+ color: var(--accent-success);
+ }
+
+ .export-section {
+ margin-top: 2rem;
+ padding-top: 2rem;
+ border-top: 2px solid var(--border-color);
+ }
+
+ .toast {
+ position: fixed;
+ bottom: 2rem;
+ right: 2rem;
+ background-color: var(--accent-success);
+ color: white;
+ padding: 1rem 2rem;
+ border-radius: 8px;
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
+ display: none;
+ animation: slideIn 0.3s;
+ z-index: 1000;
+ }
+
+ @keyframes slideIn {
+ from { transform: translateX(100%); opacity: 0; }
+ to { transform: translateX(0); opacity: 1; }
+ }
+
+ .stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ gap: 1rem;
+ margin: 1rem 0;
+ }
+
+ .stat-box {
+ text-align: center;
+ padding: 1rem;
+ background-color: var(--bg-primary);
+ border-radius: 8px;
+ border: 1px solid var(--border-color);
+ }
+
+ .stat-value {
+ font-size: 1.5rem;
+ font-weight: bold;
+ color: var(--accent-primary);
+ }
+
+ .stat-label {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ margin-top: 0.25rem;
+ }
+
+ @media (max-width: 768px) {
+ .container {
+ padding: 1rem;
+ }
+
+ h1 {
+ font-size: 1.75rem;
+ }
+
+ .evaluation-form {
+ grid-template-columns: 1fr;
+ }
+
+ .timer-section {
+ grid-template-columns: 1fr;
+ }
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <header>
+ <h1>AI Model Benchmark Evaluation System</h1>
+ <p class="subtitle">Comprehensive testing framework for data collection, research quality, reasoning, and response speed</p>
+ </header>
+
+ <div class="tabs">
+ <button class="tab active" onclick="switchTab('benchmark1')">Benchmark 1: Coding Assistants</button>
+ <button class="tab" onclick="switchTab('benchmark2')">Benchmark 2: Quantum Computing</button>
+ <button class="tab" onclick="switchTab('comparison')">Comparison Dashboard</button>
+ </div>
+
+ <!-- Benchmark 1: AI Coding Assistants -->
+ <div id="benchmark1" class="panel active">
+ <div class="card">
+ <div class="card-header">
+ <div>
+ <h2>AI Coding Assistant Market Research</h2>
+ <p style="color: var(--text-muted);">Evaluate model capability to gather, verify, and synthesize current market data</p>
+ </div>
+ <button onclick="copyPrompt('prompt1')">Copy Prompt</button>
+ </div>
+
+ <div style="position: relative;">
+ <div id="prompt1" class="prompt-box">You are being evaluated against other AI models.
+
+Task:
+Research the current state of the AI coding assistant market.
+
+Requirements:
+1. Identify the top 10 AI coding assistants available today.
+2. For each assistant provide:
+ - Company
+ - Release year
+ - Primary strengths
+ - Weaknesses
+ - Pricing
+ - Supported IDEs
+ - Supported models
+ - Context window (if available)
+ - Official website
+3. Compare all 10 in a markdown table.
+4. Rank the top 5 for:
+ - Coding
+ - Speed
+ - Accuracy
+ - Cost
+5. Include citations or sources for every factual claim.
+6. State any uncertainties rather than guessing.
+7. End with a concise executive summary (under 200 words).
+
+Evaluation criteria:
+- Accuracy
+- Completeness
+- Organization
+- Citation quality
+- Hallucination rate
+- Time to first token
+- Total completion time
+
+Output only markdown.</div>
+ <button class="copy-btn" onclick="copyToClipboard('prompt1')">Copy</button>
+ </div>
+
+ <div class="section-title">⏱️ Timing Controls</div>
+ <div class="timer-section">
+ <div class="timer-display">
+ <div class="timer-label">Time to First Token</div>
+ <div class="timer-value" id="ttf-coding">0.00s</div>
+ </div>
+ <div class="timer-display">
+ <div class="timer-label">Total Response Time</div>
+ <div class="timer-value" id="trt-coding">0.00s</div>
+ </div>
+ <div class="timer-display">
+ <div class="timer-label">Status</div>
+ <div class="timer-value" id="status-coding" style="font-size: 1.2rem; color: var(--text-muted);">Ready</div>
+ </div>
+ </div>
+
+ <div class="controls">
+ <button onclick="startTimer('coding')">Start Timer</button>
+ <button class="secondary" onclick="markFirstToken('coding')">Mark First Token</button>
+ <button class="accent" onclick="stopTimer('coding')">Stop Timer</button>
+ <button class="danger secondary" onclick="resetTimer('coding')">Reset</button>
+ </div>
+ </div>
+
+ <div class="card">
+ <h3>Evaluation Scoring</h3>
+ <div class="evaluation-form">
+ <div class="form-group">
+ <label>Model Name</label>
+ <input type="text" id="model-name-coding" placeholder="e.g., GPT-4, Claude-3, Gemini-Pro">
+ </div>
+ <div class="form-group">
+ <label>Accuracy (1-10)</label>
+ <input type="number" id="accuracy-coding" min="1" max="10" class="score-input" placeholder="1-10">
+ </div>
+ <div class="form-group">
+ <label>Completeness (1-10)</label>
+ <input type="number" id="completeness-coding" min="1" max="10" class="score-input" placeholder="1-10">
+ </div>
+ <div class="form-group">
+ <label>Citation Quality (1-10)</label>
+ <input type="number" id="citation-coding" min="1" max="10" class="score-input" placeholder="1-10">
+ </div>
+ <div class="form-group">
+ <label>Formatting (1-10)</label>
+ <input type="number" id="formatting-coding" min="1" max="10" class="score-input" placeholder="1-10">
+ </div>
+ <div class="form-group">
+ <label>Overall Usefulness (1-10)</label>
+ <input type="number" id="usefulness-coding" min="1" max="10" class="score-input" placeholder="1-10">
+ </div>
+ <div class="form-group">
+ <label>Hallucination Count</label>
+ <input type="number" id="hallucinations-coding" min="0" class="score-input" placeholder="Number of incorrect facts">
+ </div>
+ </div>
+
+ <div class="controls" style="margin-top: 2rem;">
+ <button onclick="addEvaluation('coding')">Add Evaluation</button>
+ <button class="secondary" onclick="clearForm('coding')">Clear Form</button>
+ </div>
+ </div>
+
+ <div class="card">
+ <h3>Evaluation Criteria Reference</h3>
+ <div class="comparison-table">
+ <table>
+ <thead>
+ <tr>
+ <th>Metric</th>
+ <th>How to Score</th>
+ <th>Weight</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Time to first token</td>
+ <td>Seconds (objective measurement)</td>
+ <td>Performance</td>
+ </tr>
+ <tr>
+ <td>Total response time</td>
+ <td>Seconds (objective measurement)</td>
+ <td>Performance</td>
+ </tr>
+ <tr>
+ <td>Accuracy</td>
+ <td>1–10 scale (verify against ground truth)</td>
+ <td>High</td>
+ </tr>
+ <tr>
+ <td>Completeness</td>
+ <td>1–10 scale (check all 10 assistants covered)</td>
+ <td>High</td>
+ </tr>
+ <tr>
+ <td>Citation quality</td>
+ <td>1–10 scale (verify sources exist)</td>
+ <td>High</td>
+ </tr>
+ <tr>
+ <td>Hallucinations</td>
+ <td>Count incorrect facts (lower is better)</td>
+ <td>Critical</td>
+ </tr>
+ <tr>
+ <td>Formatting</td>
+ <td>1–10 scale (markdown quality)</td>
+ <td>Medium</td>
+ </tr>
+ <tr>
+ <td>Overall usefulness</td>
+ <td>1–10 scale (practical utility)</td>
+ <td>Medium</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <!-- Benchmark 2: Quantum Computing -->
+ <div id="benchmark2" class="panel">
+ <div class="card">
+ <div class="card-header">
+ <div>
+ <h2>Quantum Computing Developments (Stress Test)</h2>
+ <p style="color: var(--text-muted);">Advanced research task requiring 15+ sources and synthesis of conflicting reports</p>
+ </div>
+ <button onclick="copyPrompt('prompt2')">Copy Prompt</button>
+ </div>
+
+ <div style="position: relative;">
+ <div id="prompt2" class="prompt-box">Research the latest developments in quantum computing during the last 12 months.
+
+Requirements:
+- Use at least 15 sources.
+- Include academic papers, company announcements, and news.
+- Distinguish confirmed facts from speculation.
+- Build a timeline.
+- Compare IBM, Google, Microsoft, Quantinuum, PsiQuantum, and IonQ.
+- Provide citations for every section.
+- Mention conflicting reports.
+- Produce a 1,500-word report with a comparison table and executive summary.
+
+This benchmark exercises:
+- Live web research (if available)
+- Data collection
+- Source verification
+- Information synthesis
+- Reasoning
+- Long-context handling
+- Speed</div>
+ <button class="copy-btn" onclick="copyToClipboard('prompt2')">Copy</button>
+ </div>
+
+ <div class="section-title">Requirements Checklist</div>
+ <ul class="checklist" id="quantum-checklist">
+ <li>At least 15 sources cited</li>
+ <li>Academic papers included</li>
+ <li>Company announcements included</li>
+ <li>News sources included</li>
+ <li>Facts distinguished from speculation</li>
+ <li>Timeline constructed</li>
+ <li>IBM analysis present</li>
+ <li>Google analysis present</li>
+ <li>Microsoft analysis present</li>
+ <li>Quantinuum analysis present</li>
+ <li>PsiQuantum analysis present</li>
+ <li>IonQ analysis present</li>
+ <li>Conflicting reports mentioned</li>
+ <li>Word count ~1,500</li>
+ <li>Comparison table included</li>
+ <li>Executive summary included</li>
+ </ul>
+
+ <div class="section-title">⏱️ Timing Controls</div>
+ <div class="timer-section">
+ <div class="timer-display">
+ <div class="timer-label">Time to First Token</div>
+ <div class="timer-value" id="ttf-quantum">0.00s</div>
+ </div>
+ <div class="timer-display">
+ <div class="timer-label">Total Response Time</div>
+ <div class="timer-value" id="trt-quantum">0.00s</div>
+ </div>
+ <div class="timer-display">
+ <div class="timer-label">Status</div>
+ <div class="timer-value" id="status-quantum" style="font-size: 1.2rem; color: var(--text-muted);">Ready</div>
+ </div>
+ </div>
+
+ <div class="controls">
+ <button onclick="startTimer('quantum')">Start Timer</button>
+ <button class="secondary" onclick="markFirstToken('quantum')">Mark First Token</button>
+ <button class="accent" onclick="stopTimer('quantum')">Stop Timer</button>
+ <button class="danger secondary" onclick="resetTimer('quantum')">Reset</button>
+ </div>
+ </div>
+
+ <div class="card">
+ <h3>Evaluation Scoring</h3>
+ <div class="evaluation-form">
+ <div class="form-group">
+ <label>Model Name</label>
+ <input type="text" id="model-name-quantum" placeholder="e.g., GPT-4, Claude-3, Gemini-Pro">
+ </div>
+ <div class="form-group">
+ <label>Source Diversity (1-10)</label>
+ <input type="number" id="sources-quantum" min="1" max="10" class="score-input" placeholder="Academic vs News vs Corporate">
+ </div>
+ <div class="form-group">
+ <label>Fact Verification (1-10)</label>
+ <input type="number" id="verification-quantum" min="1" max="10" class="score-input" placeholder="Distinguishing fact/speculation">
+ </div>
+ <div class="form-group">
+ <label>Synthesis Quality (1-10)</label>
+ <input type="number" id="synthesis-quantum" min="1" max="10" class="score-input" placeholder="Information integration">
+ </div>
+ <div class="form-group">
+ <label>Citation Accuracy (1-10)</label>
+ <input type="number" id="citation-quantum" min="1" max="10" class="score-input" placeholder="Valid sources">
+ </div>
+ <div class="form-group">
+ <label>Conflict Resolution (1-10)</label>
+ <input type="number" id="conflict-quantum" min="1" max="10" class="score-input" placeholder="Handling contradictory reports">
+ </div>
+ <div class="form-group">
+ <label>Completeness (1-10)</label>
+ <input type="number" id="completeness-quantum" min="1" max="10" class="score-input" placeholder="All companies covered">
+ </div>
+ <div class="form-group">
+ <label>Hallucination Count</label>
+ <input type="number" id="hallucinations-quantum" min="0" class="score-input" placeholder="Fabricated sources/facts">
+ </div>
+ </div>
+
+ <div class="controls" style="margin-top: 2rem;">
+ <button onclick="addEvaluation('quantum')">Add Evaluation</button>
+ <button class="secondary" onclick="clearForm('quantum')">Clear Form</button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Comparison Dashboard -->
+ <div id="comparison" class="panel">
+ <div class="card">
+ <h2>Benchmark Results Comparison</h2>
+ <div id="results-container">
+ <p style="color: var(--text-muted); text-align: center; padding: 3rem;">No evaluations recorded yet. Run benchmarks and add evaluations to see comparisons.</p>
+ </div>
+ </div>
+
+ <div class="card">
+ <h3>Aggregate Statistics</h3>
+ <div class="stats-grid" id="aggregate-stats">
+ <div class="stat-box">
+ <div class="stat-value" id="total-evaluations">0</div>
+ <div class="stat-label">Total Evaluations</div>
+ </div>
+ <div class="stat-box">
+ <div class="stat-value" id="avg-accuracy">-</div>
+ <div class="stat-label">Avg Accuracy</div>
+ </div>
+ <div class="stat-box">
+ <div class="stat-value" id="avg-speed">-</div>
+ <div class="stat-label">Avg Response Time</div>
+ </div>
+ <div class="stat-box">
+ <div class="stat-value" id="total-hallucinations">0</div>
+ <div class="stat-label">Total Hallucinations</div>
+ </div>
+ </div>
+ </div>
+
+ <div class="export-section">
+ <h3>Data Management</h3>
+ <p style="margin-bottom: 1rem; color: var(--text-secondary);">Export your evaluation data for external analysis or reporting.</p>
+ <div class="controls">
+ <button onclick="exportJSON()">Export as JSON</button>
+ <button class="secondary" onclick="exportCSV()">Export as CSV</button>
+ <button class="secondary" onclick="window.print()">Print Report</button>
+ <button class="danger secondary" onclick="clearAllData()">Clear All Data</button>
+ </div>
+ </div>
+ </div>
+
+ </div>
+
+ <div id="toast" class="toast">Copied to clipboard!</div>
+
+ <script>
+ // State management
+ let evaluations = JSON.parse(localStorage.getItem('ai_benchmark_evaluations')) || [];
+ let timers = {
+ coding: { start: null, firstToken: null, end: null, interval: null },
+ quantum: { start: null, firstToken: null, end: null, interval: null }
+ };
+
+ // Tab switching
+ function switchTab(tabName) {
+ document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
+ document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
+
+ document.getElementById(tabName).classList.add('active');
+ event.target.classList.add('active');
+
+ if (tabName === 'comparison') {
+ updateComparisonView();
+ }
+ }
+
+ // Timer functions
+ function startTimer(benchmark) {
+ const now = Date.now();
+ timers[benchmark].start = now;
+ timers[benchmark].firstToken = null;
+ timers[benchmark].end = null;
+
+ if (timers[benchmark].interval) clearInterval(timers[benchmark].interval);
+
+ timers[benchmark].interval = setInterval(() => {
+ updateTimerDisplay(benchmark);
+ }, 10);
+
+ updateStatus(benchmark, 'Running...', 'var(--accent-warning)');
+ }
+
+ function markFirstToken(benchmark) {
+ if (!timers[benchmark].start) {
+ alert('Start timer first!');
+ return;
+ }
+ timers[benchmark].firstToken = Date.now();
+ updateTimerDisplay(benchmark);
+ document.getElementById(`ttf-${benchmark}`).classList.add('stopped');
+ }
+
+ function stopTimer(benchmark) {
+ if (!timers[benchmark].start) return;
+ timers[benchmark].end = Date.now();
+ clearInterval(timers[benchmark].interval);
+ updateTimerDisplay(benchmark);
+ updateStatus(benchmark, 'Completed', 'var(--accent-success)');
+ document.getElementById(`trt-${benchmark}`).classList.add('stopped');
+ }
+
+ function resetTimer(benchmark) {
+ clearInterval(timers[benchmark].interval);
+ timers[benchmark] = { start: null, firstToken: null, end: null, interval: null };
+ document.getElementById(`ttf-${benchmark}`).textContent = '0.00s';
+ document.getElementById(`trt-${benchmark}`).textContent = '0.00s';
+ document.getElementById(`ttf-${benchmark}`).classList.remove('stopped');
+ document.getElementById(`trt-${benchmark}`).classList.remove('stopped');
+ updateStatus(benchmark, 'Ready', 'var(--text-muted)');
+ }
+
+ function updateTimerDisplay(benchmark) {
+ const now = Date.now();
+ const start = timers[benchmark].start;
+
+ if (start) {
+ const totalElapsed = (now - start) / 1000;
+ document.getElementById(`trt-${benchmark}`).textContent = totalElapsed.toFixed(2) + 's';
+
+ if (timers[benchmark].firstToken) {
+ const ttf = (timers[benchmark].firstToken - start) / 1000;
+ document.getElementById(`ttf-${benchmark}`).textContent = ttf.toFixed(2) + 's';
+ }
+ }
+ }
+
+ function updateStatus(benchmark, text, color) {
+ const statusEl = document.getElementById(`status-${benchmark}`);
+ statusEl.textContent = text;
+ statusEl.style.color = color;
+ }
+
+ // Evaluation functions
+ function addEvaluation(benchmark) {
+ const type = benchmark;
+ const modelName = document.getElementById(`model-name-${benchmark}`).value || 'Unnamed Model';
+
+ let data = {
+ id: Date.now(),
+ type: benchmark,
+ model: modelName,
+ timestamp: new Date().toISOString(),
+ timing: {
+ ttf: timers[benchmark].firstToken ? ((timers[benchmark].firstToken - timers[benchmark].start) / 1000).toFixed(2) : null,
+ total: timers[benchmark].end ? ((timers[benchmark].end - timers[benchmark].start) / 1000).toFixed(2) : null
+ }
+ };
+
+ if (benchmark === 'coding') {
+ data.scores = {
+ accuracy: parseInt(document.getElementById('accuracy-coding').value) || 0,
+ completeness: parseInt(document.getElementById('completeness-coding').value) || 0,
+ citation: parseInt(document.getElementById('citation-coding').value) || 0,
+ formatting: parseInt(document.getElementById('formatting-coding').value) || 0,
+ usefulness: parseInt(document.getElementById('usefulness-coding').value) || 0,
+ hallucinations: parseInt(document.getElementById('hallucinations-coding').value) || 0
+ };
+ } else {
+ data.scores = {
+ sources: parseInt(document.getElementById('sources-quantum').value) || 0,
+ verification: parseInt(document.getElementById('verification-quantum').value) || 0,
+ synthesis: parseInt(document.getElementById('synthesis-quantum').value) || 0,
+ citation: parseInt(document.getElementById('citation-quantum').value) || 0,
+ conflict: parseInt(document.getElementById('conflict-quantum').value) || 0,
+ completeness: parseInt(document.getElementById('completeness-quantum').value) || 0,
+ hallucinations: parseInt(document.getElementById('hallucinations-quantum').value) || 0
+ };
+ }
+
+ evaluations.push(data);
+ localStorage.setItem('ai_benchmark_evaluations', JSON.stringify(evaluations));
+
+ showToast('Evaluation added successfully!');
+ clearForm(benchmark);
+ resetTimer(benchmark);
+ }
+
+ function clearForm(benchmark) {
+ document.querySelectorAll(`#${benchmark} input[type="text"], #${benchmark} input[type="number"]`).forEach(input => {
+ input.value = '';
+ });
+ }
+
+ function updateComparisonView() {
+ const container = document.getElementById('results-container');
+
+ if (evaluations.length === 0) {
+ container.innerHTML = '<p style="color: var(--text-muted); text-align: center; padding: 3rem;">No evaluations recorded yet.</p>';
+ updateAggregateStats();
+ return;
+ }
+
+ let html = '<div style="overflow-x: auto;"><table><thead><tr>';
+ html += '<th>Model</th><th>Benchmark</th><th>TTF (s)</th><th>Total (s)</th>';
+ html += '<th>Key Scores</th><th>Hallucinations</th><th>Actions</th>';
+ html += '</tr></thead><tbody>';
+
+ evaluations.forEach((eval, index) => {
+ const scores = eval.scores;
+ let scoreDisplay = '';
+
+ if (eval.type === 'coding') {
+ scoreDisplay = `Acc:${scores.accuracy} Comp:${scores.completeness} Cit:${scores.citation}`;
+ } else {
+ scoreDisplay = `Src:${scores.sources} Syn:${scores.synthesis} Ver:${scores.verification}`;
+ }
+
+ const ttf = eval.timing.ttf || '-';
+ const total = eval.timing.total || '-';
+ const badgeClass = eval.type === 'coding' ? 'badge-success' : 'badge-warning';
+
+ html += `<tr>
+ <td><strong>${eval.model}</strong></td>
+ <td><span class="badge ${badgeClass}">${eval.type}</span></td>
+ <td>${ttf}</td>
+ <td>${total}</td>
+ <td>${scoreDisplay}</td>
+ <td style="color: ${scores.hallucinations > 0 ? 'var(--accent-danger)' : 'var(--accent-success)'}; font-weight: bold;">${scores.hallucinations}</td>
+ <td><button class="secondary" style="padding: 0.25rem 0.5rem; font-size: 0.8rem;" onclick="deleteEvaluation(${index})">Delete</button></td>
+ </tr>`;
+ });
+
+ html += '</tbody></table></div>';
+ container.innerHTML = html;
+ updateAggregateStats();
+ }
+
+ function updateAggregateStats() {
+ const total = evaluations.length;
+ document.getElementById('total-evaluations').textContent = total;
+
+ if (total === 0) {
+ document.getElementById('avg-accuracy').textContent = '-';
+ document.getElementById('avg-speed').textContent = '-';
+ document.getElementById('total-hallucinations').textContent = '0';
+ return;
+ }
+
+ let totalHallucinations = 0;
+ let totalAccuracy = 0;
+ let accuracyCount = 0;
+ let totalTime = 0;
+ let timeCount = 0;
+
+ evaluations.forEach(eval => {
+ totalHallucinations += eval.scores.hallucinations || 0;
+
+ if (eval.scores.accuracy) {
+ totalAccuracy += eval.scores.accuracy;
+ accuracyCount++;
+ }
+
+ if (eval.timing.total) {
+ totalTime += parseFloat(eval.timing.total);
+ timeCount++;
+ }
+ });
+
+ document.getElementById('avg-accuracy').textContent = accuracyCount > 0 ? (totalAccuracy / accuracyCount).toFixed(1) : '-';
+ document.getElementById('avg-speed').textContent = timeCount > 0 ? (totalTime / timeCount).toFixed(1) + 's' : '-';
+ document.getElementById('total-hallucinations').textContent = totalHallucinations;
+ }
+
+ function deleteEvaluation(index) {
+ if (confirm('Delete this evaluation?')) {
+ evaluations.splice(index, 1);
+ localStorage.setItem('ai_benchmark_evaluations', JSON.stringify(evaluations));
+ updateComparisonView();
+ }
+ }
+
+ function clearAllData() {
+ if (confirm('Clear all evaluation data? This cannot be undone.')) {
+ evaluations = [];
+ localStorage.removeItem('ai_benchmark_evaluations');
+ updateComparisonView();
+ showToast('All data cleared');
+ }
+ }
+
+ // Export functions
+ function exportJSON() {
+ const dataStr = JSON.stringify(evaluations, null, 2);
+ const blob = new Blob([dataStr], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `ai_benchmark_results_${new Date().toISOString().split('T')[0]}.json`;
+ a.click();
+ showToast('Exported to JSON');
+ }
+
+ function exportCSV() {
+ if (evaluations.length === 0) {
+ alert('No data to export');
+ return;
+ }
+
+ let csv = 'Model,Benchmark,Time_to_First_Token,Total_Time,';
+
+ // Get all possible score keys
+ const allKeys = new Set();
+ evaluations.forEach(e => Object.keys(e.scores).forEach(k => allKeys.add(k)));
+ const scoreKeys = Array.from(allKeys);
+
+ csv += scoreKeys.join(',') + ',Timestamp\n';
+
+ evaluations.forEach(e => {
+ csv += `"${e.model}","${e.type}",${e.timing.ttf || ''},${e.timing.total || ''},`;
+ scoreKeys.forEach(key => {
+ csv += (e.scores[key] || '') + ',';
+ });
+ csv += `"${e.timestamp}"\n`;
+ });
+
+ const blob = new Blob([csv], { type: 'text/csv' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `ai_benchmark_results_${new Date().toISOString().split('T')[0]}.csv`;
+ a.click();
+ showToast('Exported to CSV');
+ }
+
+ // Utility functions
+ function copyToClipboard(elementId) {
+ const text = document.getElementById(elementId).textContent;
+ navigator.clipboard.writeText(text).then(() => {
+ showToast('Prompt copied to clipboard!');
+ });
+ }
+
+ function copyPrompt(type) {
+ copyToClipboard(type === 'prompt1' ? 'prompt1' : 'prompt2');
+ }
+
+ function showToast(message) {
+ const toast = document.getElementById('toast');
+ toast.textContent = message;
+ toast.style.display = 'block';
+ setTimeout(() => {
+ toast.style.display = 'none';
+ }, 3000);
+ }
+
+ // Initialize
+ document.addEventListener('DOMContentLoaded', () => {
+ updateComparisonView();
+ });
+
+ // Keyboard shortcuts
+ document.addEventListener('keydown', (e) => {
+ if (e.ctrlKey || e.metaKey) {
+ if (e.key === '1') {
+ document.querySelector('.tab:nth-child(1)').click();
+ } else if (e.key === '2') {
+ document.querySelector('.tab:nth-child(2)').click();
+ } else if (e.key === '3') {
+ document.querySelector('.tab:nth-child(3)').click();
+ }
+ }
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/acebf42d306a/kimi.png b/data/artifacts/acebf42d306a/kimi.png
new file mode 100644
index 0000000..23c77d7
Binary files /dev/null and b/data/artifacts/acebf42d306a/kimi.png differ
diff --git a/data/artifacts/e068f31235f8/gemma3-12b.html b/data/artifacts/e068f31235f8/gemma3-12b.html
new file mode 100644
index 0000000..908cb99
--- /dev/null
+++ b/data/artifacts/e068f31235f8/gemma3-12b.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>DW Battle Arena</title>
+ <style>
+ body { font-family: sans-serif; margin: 0; padding: 0; background-color: #f4f4f4; }
+ #arena { width: 80%; max-width: 600px; margin: 20px auto; background-color: white; border-radius: 5px; overflow: hidden; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
+ #battle-log { padding: 10px; font-size: 14px; color: #333; background-color: #eee; }
+ #controls { text-align: center; padding: 10px; }
+ button { padding: 8px 15px; margin: 5px; border: none; background-color: #4CAF50; color: white; cursor: pointer; border-radius: 3px; }
+ #model-info { text-align: center; padding: 10px; font-style: italic; color: #777; }
+ </style>
+</head>
+<body>
+
+ <div id="arena">
+ <div id="battle-log">
+ <!-- Battle Log will appear here -->
+ </div>
+ <div id="controls">
+ <button onclick="startBattle()">Start Battle</button>
+ <button disabled id="attackButton">Attack!</button>
+ </div>
+ <div id="model-info">
+ Model: DW Bot (Version 1.0) - Simulating a simple battle. Click Start to begin!
+ </div>
+ </div>
+
+ <script>
+ let battleLog = document.getElementById('battle-log');
+ let attackButton = document.getElementById('attackButton');
+ let modelHealth = 100;
+ let opponentHealth = 100; //Simulated Opponent Health
+
+ function logMessage(message) {
+ const p = document.createElement('p');
+ p.textContent = message;
+ battleLog.appendChild(p);
+ battleLog.scrollTop = battleLog.scrollHeight; // Auto-scroll to bottom
+ }
+
+ function startBattle() {
+ attackButton.disabled = false;
+ logMessage("Battle started! Model Health: " + modelHealth + ", Opponent Health: " + opponentHealth);
+
+ //Simulate a simple AI interaction (replace with actual DW integration)
+ setTimeout(() => {
+ simulateOpponentAttack();
+ if(modelHealth <= 0){
+ attackButton.disabled = true;
+ logMessage("Model defeated!");
+ return;
+ }
+ }, 1500);
+
+ }
+
+
+ function simulateOpponentAttack() {
+ let damage = Math.floor(Math.random() * 15) + 5; //Random Damage
+ modelHealth -= damage;
+ logMessage("Opponent attacks! Model takes " + damage + " damage. Health: " + modelHealth);
+
+ if (modelHealth <= 0) {
+ attackButton.disabled = true;
+ logMessage("Model defeated!");
+ return;
+ }
+
+
+ //Simulate a defensive response and attack after opponent's turn
+ setTimeout(() => {
+ modelAttack();
+ }, 1000);
+
+ }
+
+ function modelAttack() {
+ let damage = Math.floor(Math.random() * 20) + 8; // Random Damage
+ opponentHealth -=damage;
+ logMessage("Model attacks! Opponent takes " + damage + " damage. Health: " + opponentHealth);
+
+
+ if (opponentHealth <= 0){
+ attackButton.disabled = true;
+ logMessage("Opponent defeated!");
+ return;
+ }
+
+ //Simulate opponent's response after Model attack.
+ setTimeout(() => {
+ simulateOpponentAttack();
+ },1500);
+
+ }
+
+
+ </script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e068f31235f8/gemma3-12b.png b/data/artifacts/e068f31235f8/gemma3-12b.png
new file mode 100644
index 0000000..0c353a3
Binary files /dev/null and b/data/artifacts/e068f31235f8/gemma3-12b.png differ
diff --git a/data/artifacts/e068f31235f8/hermes3-8b.html b/data/artifacts/e068f31235f8/hermes3-8b.html
new file mode 100644
index 0000000..634795a
--- /dev/null
+++ b/data/artifacts/e068f31235f8/hermes3-8b.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Battle to Help the Doctor Who Website</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ text-align: center;
+ background-color: #333;
+ color: white;
+ margin: 0;
+ padding: 20px;
+ }
+
+ h1 {
+ font-size: 36px;
+ margin-bottom: 10px;
+ }
+
+ .battle-container {
+ display: flex;
+ justify-content: space-around;
+ flex-wrap: wrap;
+ }
+
+ .battle-card {
+ width: calc(33% - 20px);
+ background-color: #555;
+ border-radius: 5px;
+ padding: 15px;
+ margin-bottom: 20px;
+ }
+
+ .battle-card img {
+ width: 100%;
+ height: auto;
+ border-radius: 3px;
+ }
+
+ .battle-card h2 {
+ font-size: 24px;
+ margin-top: 10px;
+ margin-bottom: 5px;
+ }
+
+ .battle-card p {
+ font-size: 16px;
+ line-height: 1.6;
+ }
+
+ .battle-card button {
+ background-color: #4CAF50;
+ color: white;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 16px;
+ }
+
+ .battle-card button:hover {
+ opacity: 0.7;
+ }
+ </style>
+</head>
+<body>
+ <h1>Battle to Help the Doctor Who Website</h1>
+ <div class="battle-container">
+ <div class="battle-card">
+ <img src="https://example.com/battle1.jpg" alt="Battle 1">
+ <h2>Alien Invasion</h2>
+ <p>The Doctor and her companions face off against a hostile alien force threatening Earth. Can they save the day?</p>
+ <button>Join Battle</button>
+ </div>
+ <div class="battle-card">
+ <img src="https://example.com/battle2.jpg" alt="Battle 2">
+ <h2>Time Paradox</h2>
+ <p>A temporal anomaly threatens to unravel the fabric of reality. The Doctor must navigate complex timelines to prevent disaster.</p>
+ <button>Join Battle</button>
+ </div>
+ <div class="battle-card">
+ <img src="https://example.com/battle3.jpg" alt="Battle 3">
+ <h2>Mystical Menace</h2>
+ <p>An ancient, powerful entity awakens, putting the universe in peril. The Doctor embarks on a quest to stop it.</p>
+ <button>Join Battle</button>
+ </div>
+ </div>
+
+ <script>
+ // No JavaScript needed for this self-contained HTML document
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e068f31235f8/hermes3-8b.png b/data/artifacts/e068f31235f8/hermes3-8b.png
new file mode 100644
index 0000000..d054f44
Binary files /dev/null and b/data/artifacts/e068f31235f8/hermes3-8b.png differ
diff --git a/data/artifacts/e068f31235f8/qwen3-14b.html b/data/artifacts/e068f31235f8/qwen3-14b.html
new file mode 100644
index 0000000..c212225
--- /dev/null
+++ b/data/artifacts/e068f31235f8/qwen3-14b.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>AI Battle Arena for DW</title>
+ <style>
+ body {
+ font-family: Arial, sans-serif;
+ text-align: center;
+ padding: 20px;
+ background-color: #f0f0f0;
+ margin: 0;
+ }
+ #arena {
+ background: white;
+ padding: 20px;
+ border-radius: 10px;
+ max-width: 600px;
+ margin: 0 auto;
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
+ }
+ h1 {
+ color: #333;
+ }
+ select, button {
+ padding: 10px 20px;
+ font-size: 16px;
+ margin-top: 10px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ background-color: #fff;
+ cursor: pointer;
+ }
+ button:hover {
+ background-color: #e0e0e0;
+ }
+ #result {
+ margin-top: 20px;
+ font-weight: bold;
+ color: #2c3e50;
+ }
+ .challenge-description {
+ margin: 15px 0;
+ color: #555;
+ }
+ .scoreboard {
+ margin: 15px 0;
+ }
+ .scoreboard div {
+ margin: 5px 0;
+ }
+ .winner {
+ font-size: 18px;
+ font-weight: bold;
+ color: #27ae60;
+ }
+ .loser {
+ font-size: 18px;
+ font-weight: bold;
+ color: #e74c3c;
+ }
+ </style>
+</head>
+<body>
+ <div id="arena">
+ <h1>AI Battle Arena</h1>
+ <select id="opponent">
+ <option value="AI_Model_X">AI Model X</option>
+ <option value="AI_Model_Y">AI Model Y</option>
+ </select>
+ <button onclick="startBattle()">Start Battle</button>
+ <div id="result"></div>
+ </div>
+ <script>
+ const challenges = [
+ {
+ name: "News Writing",
+ description: "Write a news article based on a given topic.",
+ evaluate: () => {
+ const dwScore = Math.floor(Math.random() * 10 + 80); // 80-90
+ const opponentScore = Math.floor(Math.random() * 10 + 60); // 60-70
+ return { dw: dwScore, opponent: opponentScore };
+ }
+ },
+ {
+ name: "Data Analysis",
+ description: "Analyze a dataset and provide insights.",
+ evaluate: () => {
+ const dwScore = Math.floor(Math.random() * 10 + 75); // 75-85
+ const opponentScore = Math.floor(Math.random() * 10 + 65); // 65-75
+ return { dw: dwScore, opponent: opponentScore };
+ }
+ },
+ {
+ name: "Creative Writing",
+ description: "Generate a creative story based on a prompt.",
+ evaluate: () => {
+ const dwScore = Math.floor(Math.random() * 10 + 70); // 70-80
+ const opponentScore = Math.floor(Math.random() * 10 + 70); // 70-80
+ return { dw: dwScore, opponent: opponentScore };
+ }
+ }
+ ];
+
+ function startBattle() {
+ const opponent = document.getElementById('opponent').value;
+ const challenge = challenges[Math.floor(Math.random() * challenges.length)];
+ const { dw, opponent: opponentScore } = challenge.evaluate();
+
+ let resultHTML = `<div class="challenge-description"><strong>Challenge:</strong> ${challenge.name}</div>`;
+ resultHTML += `<div class="challenge-description"><strong>Description:</strong> ${challenge.description}</div>`;
+ resultHTML += `<div class="scoreboard"><strong>DW Score:</strong> ${dw}</div>`;
+ resultHTML += `<div class="scoreboard"><strong>${opponent} Score:</strong> ${opponentScore}</div>`;
+
+ if (dw > opponentScore) {
+ resultHTML += `<div class="winner">DW wins this battle!</div>`;
+ } else if (dw < opponentScore) {
+ resultHTML += `<div class="loser">${opponent} wins this battle.</div>`;
+ } else {
+ resultHTML += `<div>It's a draw!</div>`;
+ }
+
+ document.getElementById('result').innerHTML = resultHTML;
+ }
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e068f31235f8/qwen3-14b.png b/data/artifacts/e068f31235f8/qwen3-14b.png
new file mode 100644
index 0000000..cd69567
Binary files /dev/null and b/data/artifacts/e068f31235f8/qwen3-14b.png differ
diff --git a/data/artifacts/e14b05b7754d/gpt.html b/data/artifacts/e14b05b7754d/gpt.html
new file mode 100644
index 0000000..962982f
--- /dev/null
+++ b/data/artifacts/e14b05b7754d/gpt.html
@@ -0,0 +1,493 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>Sorting Race Visualizer</title>
+<style>
+ * {
+ box-sizing: border-box;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ margin: 0;
+ padding: 0;
+ }
+ body {
+ background: #111;
+ color: #eee;
+ padding: 10px;
+ }
+ h1 {
+ text-align: center;
+ margin-bottom: 8px;
+ font-size: 20px;
+ letter-spacing: 0.05em;
+ }
+ #controls {
+ display: flex;
+ justify-content: center;
+ gap: 8px;
+ margin-bottom: 10px;
+ flex-wrap: wrap;
+ font-size: 13px;
+ }
+ button {
+ padding: 6px 12px;
+ border-radius: 4px;
+ border: 1px solid #555;
+ background: #222;
+ color: #eee;
+ cursor: pointer;
+ }
+ button:hover {
+ background: #333;
+ }
+ button:disabled {
+ opacity: 0.5;
+ cursor: default;
+ }
+ label {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ }
+ #legend {
+ display: flex;
+ justify-content: center;
+ gap: 12px;
+ font-size: 11px;
+ margin-bottom: 8px;
+ flex-wrap: wrap;
+ }
+ .legend-item {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ white-space: nowrap;
+ }
+ .box {
+ width: 14px;
+ height: 8px;
+ border-radius: 2px;
+ }
+ .cmp {
+ background: #f1c40f;
+ }
+ .swp {
+ background: #e74c3c;
+ }
+ .norm {
+ background: #3498db;
+ }
+ .done {
+ background: #2ecc71;
+ }
+ #boards {
+ display: flex;
+ gap: 8px;
+ justify-content: space-between;
+ }
+ .board {
+ background: #181818;
+ border-radius: 4px;
+ padding: 6px;
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ }
+ .title {
+ font-size: 13px;
+ margin-bottom: 4px;
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ }
+ .stats {
+ font-size: 10px;
+ color: #aaa;
+ }
+ .bar-container {
+ position: relative;
+ flex: 1;
+ display: flex;
+ align-items: flex-end;
+ gap: 1px;
+ border-radius: 3px;
+ overflow: hidden;
+ background: #101010;
+ padding: 2px 1px 1px;
+ }
+ .bar {
+ flex: 1;
+ background: #3498db;
+ transition: height 0.05s linear, background-color 0.05s linear;
+ }
+ @media (max-width: 700px) {
+ #boards {
+ flex-direction: column;
+ }
+ }
+</style>
+</head>
+<body>
+<h1>Sorting Algorithms Race</h1>
+
+<div id="controls">
+ <button id="startBtn">Start</button>
+ <button id="shuffleBtn">Shuffle</button>
+ <label>Size
+ <input id="sizeRange" type="range" min="15" max="60" value="35">
+ </label>
+ <label>Speed
+ <input id="speedRange" type="range" min="1" max="6" value="3">
+ </label>
+</div>
+
+<div id="legend">
+ <div class="legend-item"><span class="box norm"></span>Normal</div>
+ <div class="legend-item"><span class="box cmp"></span>Comparison</div>
+ <div class="legend-item"><span class="box swp"></span>Swap / Write</div>
+ <div class="legend-item"><span class="box done"></span>Sorted</div>
+</div>
+
+<div id="boards">
+ <div class="board" id="bubbleBoard">
+ <div class="title">
+ <span>Bubble Sort</span>
+ <span class="stats" id="bubbleStats"></span>
+ </div>
+ <div class="bar-container" id="bubbleBars"></div>
+ </div>
+ <div class="board" id="quickBoard">
+ <div class="title">
+ <span>Quicksort</span>
+ <span class="stats" id="quickStats"></span>
+ </div>
+ <div class="bar-container" id="quickBars"></div>
+ </div>
+ <div class="board" id="mergeBoard">
+ <div class="title">
+ <span>Merge Sort</span>
+ <span class="stats" id="mergeStats"></span>
+ </div>
+ <div class="bar-container" id="mergeBars"></div>
+ </div>
+</div>
+
+<script>
+(function () {
+ const sizeRange = document.getElementById("sizeRange");
+ const speedRange = document.getElementById("speedRange");
+ const startBtn = document.getElementById("startBtn");
+ const shuffleBtn = document.getElementById("shuffleBtn");
+
+ const boards = {
+ bubble: {
+ container: document.getElementById("bubbleBars"),
+ statsEl: document.getElementById("bubbleStats"),
+ data: [],
+ bars: [],
+ running: false,
+ comparisons: 0,
+ swaps: 0,
+ done: false
+ },
+ quick: {
+ container: document.getElementById("quickBars"),
+ statsEl: document.getElementById("quickStats"),
+ data: [],
+ bars: [],
+ running: false,
+ comparisons: 0,
+ swaps: 0,
+ done: false
+ },
+ merge: {
+ container: document.getElementById("mergeBars"),
+ statsEl: document.getElementById("mergeStats"),
+ data: [],
+ bars: [],
+ running: false,
+ comparisons: 0,
+ swaps: 0,
+ done: false
+ }
+ };
+
+ let animations = {};
+ let globalRunning = false;
+ let stepDelay = 50;
+
+ function updateDelay() {
+ const v = Number(speedRange.value);
+ const mapping = {1: 200, 2: 120, 3: 60, 4: 35, 5: 20, 6: 8};
+ stepDelay = mapping[v] || 60;
+ }
+
+ function createBaseArray(n) {
+ const arr = [];
+ for (let i = 1; i <= n; i++) arr.push(i);
+ for (let i = arr.length - 1; i > 0; i--) {
+ const j = (Math.random() * (i + 1)) | 0;
+ [arr[i], arr[j]] = [arr[j], arr[i]];
+ }
+ return arr;
+ }
+
+ function initBars(boardKey, values) {
+ const board = boards[boardKey];
+ board.container.innerHTML = "";
+ const maxVal = Math.max(...values);
+ const bars = [];
+ values.forEach(v => {
+ const bar = document.createElement("div");
+ bar.className = "bar";
+ bar.style.height = (5 + (v / maxVal) * 95) + "%";
+ board.container.appendChild(bar);
+ bars.push(bar);
+ });
+ board.data = values.slice();
+ board.bars = bars;
+ board.comparisons = 0;
+ board.swaps = 0;
+ board.done = false;
+ board.statsEl.textContent = "C:0 S:0";
+ }
+
+ function shuffleAll() {
+ if (globalRunning) return;
+ const n = Number(sizeRange.value);
+ const base = createBaseArray(n);
+ initBars("bubble", base);
+ initBars("quick", base);
+ initBars("merge", base);
+ }
+
+ function setDisabled(disabled) {
+ startBtn.disabled = disabled;
+ sizeRange.disabled = disabled;
+ }
+
+ function updateStats(name) {
+ const b = boards[name];
+ b.statsEl.textContent = "C:" + b.comparisons + " S:" + b.swaps;
+ }
+
+ function colorBars(board, indices, type) {
+ const colorMap = {
+ normal: "#3498db",
+ compare: "#f1c40f",
+ swap: "#e74c3c",
+ done: "#2ecc71"
+ };
+ const color = colorMap[type] || colorMap.normal;
+ indices.forEach(i => {
+ if (board.bars[i]) board.bars[i].style.backgroundColor = color;
+ });
+ }
+
+ function resetColors(board) {
+ board.bars.forEach(b => b.style.backgroundColor = "#3498db");
+ }
+
+ function markSorted(board) {
+ board.bars.forEach((b, idx) => {
+ setTimeout(() => {
+ b.style.backgroundColor = "#2ecc71";
+ }, idx * 10);
+ });
+ }
+
+ // Generators for each algorithm
+ function* bubbleSortGen(board) {
+ const a = board.data;
+ const n = a.length;
+ let swapped;
+ for (let i = 0; i < n - 1; i++) {
+ swapped = false;
+ for (let j = 0; j < n - i - 1; j++) {
+ board.comparisons++;
+ colorBars(board, [j, j + 1], "compare");
+ updateStats("bubble");
+ yield;
+ if (a[j] > a[j + 1]) {
+ board.swaps++;
+ [a[j], a[j + 1]] = [a[j + 1], a[j]];
+ const hj = board.bars[j].style.height;
+ const hj1 = board.bars[j + 1].style.height;
+ board.bars[j].style.height = hj1;
+ board.bars[j + 1].style.height = hj;
+ colorBars(board, [j, j + 1], "swap");
+ updateStats("bubble");
+ swapped = true;
+ yield;
+ }
+ resetColors(board);
+ }
+ if (!swapped) break;
+ }
+ }
+
+ function* quickSortGen(board) {
+ const a = board.data;
+ const stack = [[0, a.length - 1]];
+ while (stack.length) {
+ const [l, r] = stack.pop();
+ if (l >= r) continue;
+ let i = l - 1;
+ const pivot = a[r];
+ colorBars(board, [r], "compare");
+ yield;
+ for (let j = l; j < r; j++) {
+ board.comparisons++;
+ colorBars(board, [j, r], "compare");
+ updateStats("quick");
+ yield;
+ if (a[j] < pivot) {
+ i++;
+ if (i !== j) {
+ board.swaps++;
+ [a[i], a[j]] = [a[j], a[i]];
+ const hi = board.bars[i].style.height;
+ const hj = board.bars[j].style.height;
+ board.bars[i].style.height = hj;
+ board.bars[j].style.height = hi;
+ colorBars(board, [i, j], "swap");
+ updateStats("quick");
+ yield;
+ }
+ }
+ resetColors(board);
+ }
+ const p = i + 1;
+ board.swaps++;
+ [a[p], a[r]] = [a[r], a[p]];
+ const hp = board.bars[p].style.height;
+ const hr = board.bars[r].style.height;
+ board.bars[p].style.height = hr;
+ board.bars[r].style.height = hp;
+ colorBars(board, [p, r], "swap");
+ updateStats("quick");
+ yield;
+ resetColors(board);
+ stack.push([l, p - 1]);
+ stack.push([p + 1, r]);
+ }
+ }
+
+ function* mergeSortGen(board) {
+ const a = board.data;
+ const n = a.length;
+ const aux = a.slice();
+ const stack = [];
+ let width = 1;
+ while (width < n) {
+ let left = 0;
+ while (left < n - width) {
+ const mid = left + width;
+ const right = Math.min(left + 2 * width, n);
+ stack.push({left, mid, right});
+ left += 2 * width;
+ }
+ width *= 2;
+ }
+ // simulate bottom-up merges in order:
+ width = 1;
+ while (width < n) {
+ let left = 0;
+ while (left < n - width) {
+ const mid = left + width;
+ const right = Math.min(left + 2 * width, n);
+ let i = left, j = mid, k = left;
+ while (i < mid && j < right) {
+ board.comparisons++;
+ colorBars(board, [i, j], "compare");
+ updateStats("merge");
+ yield;
+ if (a[i] <= a[j]) {
+ aux[k++] = a[i++];
+ } else {
+ aux[k++] = a[j++];
+ }
+ resetColors(board);
+ }
+ while (i < mid) aux[k++] = a[i++];
+ while (j < right) aux[k++] = a[j++];
+ for (let t = left; t < right; t++) {
+ if (a[t] !== aux[t]) {
+ board.swaps++;
+ a[t] = aux[t];
+ // animate write
+ board.bars[t].style.height = (5 + (a[t] / n) * 95) + "%";
+ colorBars(board, [t], "swap");
+ updateStats("merge");
+ yield;
+ resetColors(board);
+ }
+ }
+ left += 2 * width;
+ }
+ width *= 2;
+ }
+ }
+
+ function startAlgorithm(name, genFactory) {
+ const board = boards[name];
+ board.running = true;
+ board.done = false;
+ resetColors(board);
+ const gen = genFactory(board);
+ animations[name] = gen;
+
+ function step() {
+ if (!board.running) return;
+ const {value, done} = gen.next();
+ if (done) {
+ board.running = false;
+ board.done = true;
+ markSorted(board);
+ checkAllDone();
+ return;
+ }
+ const delay = stepDelay;
+ setTimeout(step, delay);
+ }
+ step();
+ }
+
+ function checkAllDone() {
+ if (boards.bubble.done && boards.quick.done && boards.merge.done) {
+ globalRunning = false;
+ startBtn.textContent = "Start";
+ setDisabled(false);
+ }
+ }
+
+ function startRace() {
+ if (globalRunning) {
+ // stop
+ globalRunning = false;
+ Object.keys(boards).forEach(k => boards[k].running = false);
+ startBtn.textContent = "Start";
+ setDisabled(false);
+ return;
+ }
+ globalRunning = true;
+ setDisabled(true);
+ startBtn.disabled = false;
+ startBtn.textContent = "Stop";
+ startAlgorithm("bubble", bubbleSortGen);
+ startAlgorithm("quick", quickSortGen);
+ startAlgorithm("merge", mergeSortGen);
+ }
+
+ startBtn.addEventListener("click", startRace);
+ shuffleBtn.addEventListener("click", shuffleAll);
+ sizeRange.addEventListener("input", shuffleAll);
+ speedRange.addEventListener("input", updateDelay);
+
+ updateDelay();
+ shuffleAll();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e14b05b7754d/gpt.png b/data/artifacts/e14b05b7754d/gpt.png
new file mode 100644
index 0000000..fcb8c81
Binary files /dev/null and b/data/artifacts/e14b05b7754d/gpt.png differ
diff --git a/data/artifacts/e14b05b7754d/grok.html b/data/artifacts/e14b05b7754d/grok.html
new file mode 100644
index 0000000..6ee1d30
--- /dev/null
+++ b/data/artifacts/e14b05b7754d/grok.html
@@ -0,0 +1,435 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Sorting Race</title>
+<style>
+ * { box-sizing: border-box; margin: 0; padding: 0; }
+ body {
+ font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
+ background: #0f1221;
+ color: #e8eaf0;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 16px;
+ }
+ h1 {
+ font-size: 1.35rem;
+ font-weight: 600;
+ letter-spacing: 0.02em;
+ margin-bottom: 12px;
+ color: #c8cce0;
+ }
+ .controls {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 16px;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+ }
+ button {
+ background: #3b5bdb;
+ color: #fff;
+ border: none;
+ border-radius: 8px;
+ padding: 10px 22px;
+ font-size: 0.95rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background 0.15s, transform 0.1s;
+ }
+ button:hover:not(:disabled) { background: #4c6ef5; }
+ button:active:not(:disabled) { transform: scale(0.97); }
+ button:disabled { opacity: 0.45; cursor: not-allowed; }
+ button.secondary { background: #373a4d; }
+ button.secondary:hover:not(:disabled) { background: #4a4e66; }
+ .legend {
+ display: flex;
+ gap: 14px;
+ font-size: 0.78rem;
+ color: #9aa0b8;
+ margin-bottom: 14px;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+ .legend span { display: flex; align-items: center; gap: 5px; }
+ .swatch {
+ width: 12px; height: 12px; border-radius: 3px; display: inline-block;
+ }
+ .arena {
+ display: flex;
+ gap: 12px;
+ width: 100%;
+ max-width: 1100px;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+ .panel {
+ flex: 1 1 300px;
+ min-width: 280px;
+ max-width: 360px;
+ background: #181b2e;
+ border-radius: 12px;
+ padding: 12px;
+ border: 1px solid #2a2e45;
+ }
+ .panel h2 {
+ font-size: 0.95rem;
+ font-weight: 600;
+ text-align: center;
+ margin-bottom: 4px;
+ color: #d0d4ec;
+ }
+ .stats {
+ text-align: center;
+ font-size: 0.72rem;
+ color: #7a809a;
+ margin-bottom: 8px;
+ min-height: 1.1em;
+ }
+ .stats.done { color: #69db7c; font-weight: 600; }
+ canvas {
+ width: 100%;
+ height: 220px;
+ display: block;
+ border-radius: 6px;
+ background: #12152a;
+ }
+</style>
+</head>
+<body>
+ <h1>Sorting Algorithms Race</h1>
+ <div class="controls">
+ <button id="btnShuffle" class="secondary">Shuffle</button>
+ <button id="btnStart">Start</button>
+ </div>
+ <div class="legend">
+ <span><i class="swatch" style="background:#748ffc"></i> Default</span>
+ <span><i class="swatch" style="background:#ffd43b"></i> Comparing</span>
+ <span><i class="swatch" style="background:#ff6b6b"></i> Swap / Write</span>
+ <span><i class="swatch" style="background:#69db7c"></i> Sorted</span>
+ </div>
+ <div class="arena">
+ <div class="panel">
+ <h2>Bubble Sort</h2>
+ <div class="stats" id="stats0">Ready</div>
+ <canvas id="c0" width="340" height="220"></canvas>
+ </div>
+ <div class="panel">
+ <h2>Quicksort</h2>
+ <div class="stats" id="stats1">Ready</div>
+ <canvas id="c1" width="340" height="220"></canvas>
+ </div>
+ <div class="panel">
+ <h2>Merge Sort</h2>
+ <div class="stats" id="stats2">Ready</div>
+ <canvas id="c2" width="340" height="220"></canvas>
+ </div>
+ </div>
+<script>
+(function () {
+ const N = 40;
+ const COLORS = {
+ base: '#748ffc',
+ compare: '#ffd43b',
+ swap: '#ff6b6b',
+ sorted: '#69db7c',
+ pivot: '#da77f2'
+ };
+
+ const canvases = [0, 1, 2].map(i => document.getElementById('c' + i));
+ const ctxs = canvases.map(c => c.getContext('2d'));
+ const statsEls = [0, 1, 2].map(i => document.getElementById('stats' + i));
+ const btnStart = document.getElementById('btnStart');
+ const btnShuffle = document.getElementById('btnShuffle');
+
+ let baseArray = [];
+ let arrays = [[], [], []];
+ let colors = [[], [], []];
+ let running = false;
+ let finished = [false, false, false];
+ let comparisons = [0, 0, 0];
+ let swaps = [0, 0, 0];
+
+ function shuffleBase() {
+ baseArray = Array.from({ length: N }, (_, i) => i + 1);
+ for (let i = N - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [baseArray[i], baseArray[j]] = [baseArray[j], baseArray[i]];
+ }
+ }
+
+ function resetArrays() {
+ for (let k = 0; k < 3; k++) {
+ arrays[k] = baseArray.slice();
+ colors[k] = Array(N).fill(COLORS.base);
+ finished[k] = false;
+ comparisons[k] = 0;
+ swaps[k] = 0;
+ statsEls[k].textContent = 'Ready';
+ statsEls[k].classList.remove('done');
+ }
+ drawAll();
+ }
+
+ function drawOne(k) {
+ const ctx = ctxs[k];
+ const canvas = canvases[k];
+ const W = canvas.width;
+ const H = canvas.height;
+ const arr = arrays[k];
+ const col = colors[k];
+ ctx.clearRect(0, 0, W, H);
+ const gap = 1;
+ const barW = (W - gap * (N + 1)) / N;
+ const maxV = N;
+ for (let i = 0; i < N; i++) {
+ const h = (arr[i] / maxV) * (H - 8);
+ const x = gap + i * (barW + gap);
+ const y = H - h;
+ ctx.fillStyle = col[i];
+ ctx.beginPath();
+ const r = Math.min(3, barW / 2);
+ roundRect(ctx, x, y, barW, h, r);
+ ctx.fill();
+ }
+ }
+
+ function roundRect(ctx, x, y, w, h, r) {
+ if (h < r) r = h;
+ if (w < r) r = w;
+ ctx.moveTo(x + r, y);
+ ctx.arcTo(x + w, y, x + w, y + h, r);
+ ctx.arcTo(x + w, y + h, x, y + h, r);
+ ctx.arcTo(x, y + h, x, y, r);
+ ctx.arcTo(x, y, x + w, y, r);
+ ctx.closePath();
+ }
+
+ function drawAll() {
+ for (let k = 0; k < 3; k++) drawOne(k);
+ }
+
+ function sleep(ms) {
+ return new Promise(r => setTimeout(r, ms));
+ }
+
+ function updateStats(k) {
+ if (finished[k]) {
+ statsEls[k].textContent = 'Done — ' + comparisons[k] + ' cmp, ' + swaps[k] + ' writes';
+ statsEls[k].classList.add('done');
+ } else {
+ statsEls[k].textContent = comparisons[k] + ' cmp · ' + swaps[k] + ' writes';
+ }
+ }
+
+ async function markSorted(k) {
+ for (let i = 0; i < N; i++) {
+ colors[k][i] = COLORS.sorted;
+ if (i % 3 === 0) { drawOne(k); await sleep(8); }
+ }
+ drawOne(k);
+ finished[k] = true;
+ updateStats(k);
+ }
+
+ // ---- Bubble Sort ----
+ async function bubbleSort(k) {
+ const a = arrays[k];
+ const c = colors[k];
+ for (let i = 0; i < N - 1; i++) {
+ if (!running) return;
+ for (let j = 0; j < N - i - 1; j++) {
+ if (!running) return;
+ c[j] = COLORS.compare;
+ c[j + 1] = COLORS.compare;
+ comparisons[k]++;
+ updateStats(k);
+ drawOne(k);
+ await sleep(12);
+ if (a[j] > a[j + 1]) {
+ c[j] = COLORS.swap;
+ c[j + 1] = COLORS.swap;
+ [a[j], a[j + 1]] = [a[j + 1], a[j]];
+ swaps[k]++;
+ updateStats(k);
+ drawOne(k);
+ await sleep(12);
+ }
+ c[j] = COLORS.base;
+ c[j + 1] = COLORS.base;
+ }
+ c[N - i - 1] = COLORS.sorted;
+ drawOne(k);
+ }
+ c[0] = COLORS.sorted;
+ await markSorted(k);
+ }
+
+ // ---- Quicksort ----
+ async function quicksort(k) {
+ const a = arrays[k];
+ const c = colors[k];
+
+ async function partition(lo, hi) {
+ const pivot = a[hi];
+ c[hi] = COLORS.pivot;
+ let i = lo;
+ for (let j = lo; j < hi; j++) {
+ if (!running) return i;
+ c[j] = COLORS.compare;
+ comparisons[k]++;
+ updateStats(k);
+ drawOne(k);
+ await sleep(14);
+ if (a[j] < pivot) {
+ c[i] = COLORS.swap;
+ c[j] = COLORS.swap;
+ [a[i], a[j]] = [a[j], a[i]];
+ swaps[k]++;
+ updateStats(k);
+ drawOne(k);
+ await sleep(14);
+ c[i] = COLORS.base;
+ c[j] = COLORS.base;
+ i++;
+ } else {
+ c[j] = COLORS.base;
+ }
+ }
+ c[i] = COLORS.swap;
+ c[hi] = COLORS.swap;
+ [a[i], a[hi]] = [a[hi], a[i]];
+ swaps[k]++;
+ updateStats(k);
+ drawOne(k);
+ await sleep(14);
+ c[i] = COLORS.sorted;
+ if (hi !== i) c[hi] = COLORS.base;
+ drawOne(k);
+ return i;
+ }
+
+ async function qs(lo, hi) {
+ if (!running) return;
+ if (lo < hi) {
+ const p = await partition(lo, hi);
+ if (!running) return;
+ await qs(lo, p - 1);
+ await qs(p + 1, hi);
+ } else if (lo === hi && lo >= 0 && lo < N) {
+ c[lo] = COLORS.sorted;
+ drawOne(k);
+ }
+ }
+
+ await qs(0, N - 1);
+ if (running) await markSorted(k);
+ }
+
+ // ---- Merge Sort ----
+ async function mergeSort(k) {
+ const a = arrays[k];
+ const c = colors[k];
+
+ async function merge(lo, mid, hi) {
+ const left = a.slice(lo, mid + 1);
+ const right = a.slice(mid + 1, hi + 1);
+ let i = 0, j = 0, p = lo;
+ while (i < left.length && j < right.length) {
+ if (!running) return;
+ c[p] = COLORS.compare;
+ if (lo + i <= mid) c[lo + i] = COLORS.compare;
+ if (mid + 1 + j <= hi) c[mid + 1 + j] = COLORS.compare;
+ comparisons[k]++;
+ updateStats(k);
+ drawOne(k);
+ await sleep(14);
+ if (left[i] <= right[j]) {
+ a[p] = left[i++];
+ } else {
+ a[p] = right[j++];
+ }
+ c[p] = COLORS.swap;
+ swaps[k]++;
+ updateStats(k);
+ drawOne(k);
+ await sleep(14);
+ c[p] = COLORS.base;
+ p++;
+ }
+ while (i < left.length) {
+ if (!running) return;
+ a[p] = left[i++];
+ c[p] = COLORS.swap;
+ swaps[k]++;
+ updateStats(k);
+ drawOne(k);
+ await sleep(12);
+ c[p] = COLORS.base;
+ p++;
+ }
+ while (j < right.length) {
+ if (!running) return;
+ a[p] = right[j++];
+ c[p] = COLORS.swap;
+ swaps[k]++;
+ updateStats(k);
+ drawOne(k);
+ await sleep(12);
+ c[p] = COLORS.base;
+ p++;
+ }
+ }
+
+ async function ms(lo, hi) {
+ if (!running) return;
+ if (lo >= hi) return;
+ const mid = (lo + hi) >> 1;
+ await ms(lo, mid);
+ await ms(mid + 1, hi);
+ await merge(lo, mid, hi);
+ }
+
+ await ms(0, N - 1);
+ if (running) await markSorted(k);
+ }
+
+ async function startRace() {
+ if (running) return;
+ resetArrays();
+ running = true;
+ btnStart.disabled = true;
+ btnShuffle.disabled = true;
+
+ await Promise.all([
+ bubbleSort(0),
+ quicksort(1),
+ mergeSort(2)
+ ]);
+
+ running = false;
+ btnStart.disabled = false;
+ btnShuffle.disabled = false;
+ btnStart.textContent = 'Start';
+ }
+
+ function doShuffle() {
+ if (running) return;
+ shuffleBase();
+ resetArrays();
+ }
+
+ btnStart.addEventListener('click', startRace);
+ btnShuffle.addEventListener('click', doShuffle);
+
+ shuffleBase();
+ resetArrays();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e14b05b7754d/grok.png b/data/artifacts/e14b05b7754d/grok.png
new file mode 100644
index 0000000..b1af623
Binary files /dev/null and b/data/artifacts/e14b05b7754d/grok.png differ
diff --git a/data/artifacts/e14b05b7754d/kimi.html b/data/artifacts/e14b05b7754d/kimi.html
new file mode 100644
index 0000000..d56d96f
--- /dev/null
+++ b/data/artifacts/e14b05b7754d/kimi.html
@@ -0,0 +1,409 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Sorting Algorithm Race</title>
+ <style>
+ * { box-sizing: border-box; }
+ body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ margin: 0;
+ padding: 20px;
+ background: #1a1a2e;
+ color: #eee;
+ }
+ .container {
+ max-width: 1400px;
+ margin: 0 auto;
+ }
+ h1 {
+ text-align: center;
+ color: #fff;
+ margin-bottom: 10px;
+ font-size: 2em;
+ }
+ .controls {
+ text-align: center;
+ margin-bottom: 30px;
+ }
+ button {
+ background: #e94560;
+ color: white;
+ border: none;
+ padding: 12px 30px;
+ font-size: 16px;
+ cursor: pointer;
+ border-radius: 6px;
+ margin: 0 10px;
+ font-weight: bold;
+ transition: background 0.2s;
+ }
+ button:hover {
+ background: #c73e54;
+ }
+ button:disabled {
+ background: #555;
+ cursor: not-allowed;
+ }
+ .racing-track {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 20px;
+ }
+ .sorter-container {
+ background: #16213e;
+ padding: 20px;
+ border-radius: 10px;
+ box-shadow: 0 8px 16px rgba(0,0,0,0.3);
+ border: 1px solid #0f3460;
+ }
+ .sorter-container h2 {
+ margin-top: 0;
+ text-align: center;
+ color: #e94560;
+ font-size: 1.4em;
+ margin-bottom: 15px;
+ }
+ canvas {
+ width: 100%;
+ height: 450px;
+ background: #0a0a14;
+ border-radius: 4px;
+ display: block;
+ border: 1px solid #0f3460;
+ }
+ .legend {
+ display: flex;
+ justify-content: center;
+ gap: 15px;
+ margin-top: 15px;
+ font-size: 13px;
+ flex-wrap: wrap;
+ }
+ .legend-item {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+ .color-box {
+ width: 16px;
+ height: 16px;
+ border-radius: 3px;
+ border: 1px solid rgba(255,255,255,0.2);
+ }
+ @media (max-width: 900px) {
+ .racing-track { grid-template-columns: 1fr; }
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <h1>🏁 Sorting Algorithm Race</h1>
+ <div class="controls">
+ <button id="startBtn" onclick="race.start()">Start Race</button>
+ <button id="shuffleBtn" onclick="race.shuffle()">Shuffle Array</button>
+ </div>
+ <div class="racing-track">
+ <div class="sorter-container">
+ <h2>Bubble Sort</h2>
+ <canvas id="bubbleCanvas" width="400" height="450"></canvas>
+ <div class="legend">
+ <div class="legend-item">
+ <div class="color-box" style="background: #f1c40f;"></div>
+ <span>Compare</span>
+ </div>
+ <div class="legend-item">
+ <div class="color-box" style="background: #e74c3c;"></div>
+ <span>Swap</span>
+ </div>
+ <div class="legend-item">
+ <div class="color-box" style="background: #2ecc71;"></div>
+ <span>Sorted</span>
+ </div>
+ </div>
+ </div>
+ <div class="sorter-container">
+ <h2>Quick Sort</h2>
+ <canvas id="quickCanvas" width="400" height="450"></canvas>
+ <div class="legend">
+ <div class="legend-item">
+ <div class="color-box" style="background: #f1c40f;"></div>
+ <span>Compare</span>
+ </div>
+ <div class="legend-item">
+ <div class="color-box" style="background: #e74c3c;"></div>
+ <span>Swap</span>
+ </div>
+ <div class="legend-item">
+ <div class="color-box" style="background: #2ecc71;"></div>
+ <span>Sorted</span>
+ </div>
+ </div>
+ </div>
+ <div class="sorter-container">
+ <h2>Merge Sort</h2>
+ <canvas id="mergeCanvas" width="400" height="450"></canvas>
+ <div class="legend">
+ <div class="legend-item">
+ <div class="color-box" style="background: #f1c40f;"></div>
+ <span>Compare</span>
+ </div>
+ <div class="legend-item">
+ <div class="color-box" style="background: #e74c3c;"></div>
+ <span>Write</span>
+ </div>
+ <div class="legend-item">
+ <div class="color-box" style="background: #2ecc71;"></div>
+ <span>Sorted</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <script>
+ class Sorter {
+ constructor(canvasId, delay = 15) {
+ this.canvas = document.getElementById(canvasId);
+ this.ctx = this.canvas.getContext('2d');
+ this.delay = delay;
+ this.array = [];
+ this.comparing = new Set();
+ this.swapping = new Set();
+ this.sorted = new Set();
+ this.running = false;
+ this.size = 35;
+ }
+
+ setArray(arr) {
+ this.array = [...arr];
+ this.comparing.clear();
+ this.swapping.clear();
+ this.sorted.clear();
+ this.draw();
+ }
+
+ stop() {
+ this.running = false;
+ }
+
+ draw() {
+ const ctx = this.ctx;
+ const canvas = this.canvas;
+ const array = this.array;
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ if (array.length === 0) return;
+
+ const barWidth = canvas.width / array.length;
+ const maxVal = 100;
+ const scale = (canvas.height - 40) / maxVal;
+
+ for (let i = 0; i < array.length; i++) {
+ const height = array[i] * scale;
+ const x = i * barWidth;
+ const y = canvas.height - height - 10;
+
+ if (this.sorted.has(i)) {
+ ctx.fillStyle = '#2ecc71';
+ } else if (this.swapping.has(i)) {
+ ctx.fillStyle = '#e74c3c';
+ } else if (this.comparing.has(i)) {
+ ctx.fillStyle = '#f1c40f';
+ } else {
+ ctx.fillStyle = '#3498db';
+ }
+
+ ctx.fillRect(x + 1, y, barWidth - 2, height);
+ }
+ }
+
+ async sleep() {
+ this.draw();
+ if (!this.running) throw new Error('Stopped');
+ await new Promise(resolve => setTimeout(resolve, this.delay));
+ }
+
+ async compare(i, j) {
+ this.comparing.add(i);
+ this.comparing.add(j);
+ await this.sleep();
+ this.comparing.delete(i);
+ this.comparing.delete(j);
+ }
+
+ async swap(i, j) {
+ this.swapping.add(i);
+ this.swapping.add(j);
+ [this.array[i], this.array[j]] = [this.array[j], this.array[i]];
+ await this.sleep();
+ this.swapping.delete(i);
+ this.swapping.delete(j);
+ }
+
+ async write(index, value) {
+ this.swapping.add(index);
+ this.array[index] = value;
+ await this.sleep();
+ this.swapping.delete(index);
+ }
+
+ async setSorted(index) {
+ this.sorted.add(index);
+ this.draw();
+ }
+
+ async bubbleSort() {
+ this.running = true;
+ const n = this.array.length;
+ try {
+ for (let i = 0; i < n - 1; i++) {
+ for (let j = 0; j < n - i - 1; j++) {
+ await this.compare(j, j + 1);
+ if (this.array[j] > this.array[j + 1]) {
+ await this.swap(j, j + 1);
+ }
+ }
+ await this.setSorted(n - i - 1);
+ }
+ await this.setSorted(0);
+ } catch (e) {}
+ this.running = false;
+ }
+
+ async quickSort() {
+ this.running = true;
+ try {
+ await this._quickSort(0, this.array.length - 1);
+ for (let i = 0; i < this.array.length; i++) {
+ await this.setSorted(i);
+ }
+ } catch (e) {}
+ this.running = false;
+ }
+
+ async _quickSort(left, right) {
+ if (left < right) {
+ const pi = await this._partition(left, right);
+ await this.setSorted(pi);
+ await this._quickSort(left, pi - 1);
+ await this._quickSort(pi + 1, right);
+ } else if (left === right) {
+ await this.setSorted(left);
+ }
+ }
+
+ async _partition(left, right) {
+ const pivot = this.array[right];
+ let i = left - 1;
+ for (let j = left; j < right; j++) {
+ await this.compare(j, right);
+ if (this.array[j] < pivot) {
+ i++;
+ await this.swap(i, j);
+ }
+ }
+ await this.swap(i + 1, right);
+ return i + 1;
+ }
+
+ async mergeSort() {
+ this.running = true;
+ const temp = new Array(this.array.length);
+ try {
+ await this._mergeSort(temp, 0, this.array.length - 1);
+ for (let i = 0; i < this.array.length; i++) {
+ await this.setSorted(i);
+ }
+ } catch (e) {}
+ this.running = false;
+ }
+
+ async _mergeSort(temp, left, right) {
+ if (left < right) {
+ const center = Math.floor((left + right) / 2);
+ await this._mergeSort(temp, left, center);
+ await this._mergeSort(temp, center + 1, right);
+ await this._merge(temp, left, center + 1, right);
+ }
+ }
+
+ async _merge(temp, left, right, rightEnd) {
+ const leftEnd = right - 1;
+ let k = left;
+ const num = rightEnd - left + 1;
+
+ while (left <= leftEnd && right <= rightEnd) {
+ await this.compare(left, right);
+ if (this.array[left] <= this.array[right]) {
+ temp[k] = this.array[left];
+ left++;
+ } else {
+ temp[k] = this.array[right];
+ right++;
+ }
+ k++;
+ }
+
+ while (left <= leftEnd) {
+ temp[k] = this.array[left];
+ k++;
+ left++;
+ }
+
+ while (right <= rightEnd) {
+ temp[k] = this.array[right];
+ k++;
+ right++;
+ }
+
+ for (let i = 0; i < num; i++) {
+ const idx = rightEnd - i;
+ await this.write(idx, temp[idx]);
+ }
+ }
+ }
+
+ const race = {
+ bubble: new Sorter('bubbleCanvas', 20),
+ quick: new Sorter('quickCanvas', 20),
+ merge: new Sorter('mergeCanvas', 20),
+
+ init() {
+ this.shuffle();
+ },
+
+ shuffle() {
+ this.bubble.stop();
+ this.quick.stop();
+ this.merge.stop();
+
+ document.getElementById('startBtn').disabled = false;
+
+ const size = 35;
+ const baseArray = Array.from({length: size}, () => Math.floor(Math.random() * 90) + 10);
+
+ setTimeout(() => {
+ this.bubble.setArray([...baseArray]);
+ this.quick.setArray([...baseArray]);
+ this.merge.setArray([...baseArray]);
+ }, 50);
+ },
+
+ start() {
+ if (this.bubble.running || this.quick.running || this.merge.running) return;
+
+ document.getElementById('startBtn').disabled = true;
+
+ this.bubble.bubbleSort();
+ this.quick.quickSort();
+ this.merge.mergeSort();
+ }
+ };
+
+ window.onload = () => race.init();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e14b05b7754d/kimi.png b/data/artifacts/e14b05b7754d/kimi.png
new file mode 100644
index 0000000..98ebd45
Binary files /dev/null and b/data/artifacts/e14b05b7754d/kimi.png differ
diff --git a/data/artifacts/e14b05b7754d/qwen25-7b.html b/data/artifacts/e14b05b7754d/qwen25-7b.html
new file mode 100644
index 0000000..49d2a33
--- /dev/null
+++ b/data/artifacts/e14b05b7754d/qwen25-7b.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Sorting Algorithms Race</title>
+ <style>
+ body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f0f0; }
+ .chart-container { width: 80%; max-width: 960px; margin-bottom: 20px; }
+ canvas { border: 1px solid #ccc; padding: 5px; }
+ button { margin-top: 20px; padding: 10px 20px; font-size: 16px; cursor: pointer; }
+ </style>
+</head>
+<body>
+ <h1>Sorting Algorithms Race</h1>
+ <div class="chart-container">
+ <canvas id="bubbleChart" width="300" height="400"></canvas>
+ <label>Bubble Sort</label>
+ </div>
+ <div class="chart-container">
+ <canvas id="quickSortChart" width="300" height="400"></canvas>
+ <label>Quick Sort</label>
+ </div>
+ <div class="chart-container">
+ <canvas id="mergeSortChart" width="300" height="400"></canvas>
+ <label>Merge Sort</label>
+ </div>
+ <button onclick="shuffleAndRun()">Start/Shuffle</button>
+
+ <script>
+ const barWidth = 25;
+ const canvas = [document.getElementById('bubbleChart'), document.getElementById('quickSortChart'), document.getElementById('mergeSortChart')];
+ const ctx = canvas.map(c => c.getContext('2d'));
+
+ function shuffleAndRun() {
+ let data = Array.from({ length: 100 }, () => Math.floor(Math.random() * 400));
+ runAlgorithm(data, 0, 'Bubble Sort');
+ runAlgorithm(data.slice(), 0, 'Quick Sort');
+ runAlgorithm(data.slice(), 0, 'Merge Sort');
+ }
+
+ function drawBars(arr) {
+ ctx.forEach((c, i) => {
+ c.clearRect(0, 0, canvas[i].width, canvas[i].height);
+ arr.forEach((h, j) => {
+ let x = barWidth * (j + 1);
+ c.fillStyle = i === 2 ? 'blue' : i === 1 ? 'green' : '#f06';
+ if (i < 2 && h > arr[j - 1]) c.fillStyle += '-comp';
+ else if (i === 2 && j < arr.length - 1) c.fillStyle += '-swp';
+ c.fillRect(x, canvas[i].height - h, barWidth, h);
+ });
+ });
+ }
+
+ function runAlgorithm(arr, index, name) {
+ let algo = {
+ Bubble: bubbleSort,
+ Quick: quickSort,
+ Merge: mergeSort
+ }[name];
+ drawBars([...arr]);
+ if (index === arr.length - 1) return;
+ setTimeout(() => {
+ algo.call(null, [...arr], index + 1);
+ }, 50);
+ }
+
+ function bubbleSort(arr, i = 0) {
+ if (i === arr.length - 1) return;
+ for (let j = 0; j < arr.length - i - 1; j++) {
+ drawBars([...arr]);
+ if (arr[j] > arr[j + 1]) [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
+ }
+ setTimeout(() => bubbleSort(arr, i + 1), 50);
+ }
+
+ function quickSort(arr, index = 0) {
+ if (index >= arr.length - 1) return;
+ let pivot = partition(arr, index);
+ drawBars([...arr]);
+ quickSort(arr.slice(0, pivot), index);
+ quickSort(arr.slice(pivot + 1), pivot + 1);
+ }
+
+ function partition(arr, i = 0) {
+ let pivot = arr[i], left = -1;
+ for (let j = i; j < arr.length; j++) {
+ drawBars([...arr]);
+ if (arr[j] <= pivot) [arr[++left], arr[j]] = [arr[j], arr[left]];
+ }
+ return left;
+ }
+
+ function mergeSort(arr, index = 0) {
+ if (index >= arr.length - 1) return;
+ let mid = Math.floor((arr.length - index) / 2 + index);
+ drawBars([...arr]);
+ mergeSort(arr.slice(index, mid), index);
+ mergeSort(arr.slice(mid), mid);
+ merge(arr, index, mid);
+ }
+
+ function merge(arr, start, mid) {
+ let left = arr.slice(start, mid), right = arr.slice(mid), i = 0, j = 0;
+ while (i < left.length && j < right.length) {
+ drawBars([...arr]);
+ if (left[i] <= right[j]) [arr[start++], left[i++]] = [left[i], arr[start - 1]];
+ else [arr[start++], right[j++]] = [right[j], arr[start - 1]];
+ }
+ }
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e14b05b7754d/qwen25-7b.png b/data/artifacts/e14b05b7754d/qwen25-7b.png
new file mode 100644
index 0000000..f1d5989
Binary files /dev/null and b/data/artifacts/e14b05b7754d/qwen25-7b.png differ
diff --git a/data/artifacts/e4acfac81892/gemma3-12b.html b/data/artifacts/e4acfac81892/gemma3-12b.html
new file mode 100644
index 0000000..5f86a55
--- /dev/null
+++ b/data/artifacts/e4acfac81892/gemma3-12b.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head><title>Color Response</title></head>
+<body>
+<p>Blue.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e4acfac81892/gemma3-12b.png b/data/artifacts/e4acfac81892/gemma3-12b.png
new file mode 100644
index 0000000..1a98a46
Binary files /dev/null and b/data/artifacts/e4acfac81892/gemma3-12b.png differ
diff --git a/data/artifacts/e4acfac81892/gpt.html b/data/artifacts/e4acfac81892/gpt.html
new file mode 100644
index 0000000..ecd95f7
--- /dev/null
+++ b/data/artifacts/e4acfac81892/gpt.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Color</title>
+<style>
+ body{margin:0;font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;background:#111;color:#fafafa}
+</style>
+</head>
+<body>
+<script>
+ document.body.textContent = "Blue is a color.";
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e4acfac81892/gpt.png b/data/artifacts/e4acfac81892/gpt.png
new file mode 100644
index 0000000..52bbddf
Binary files /dev/null and b/data/artifacts/e4acfac81892/gpt.png differ
diff --git a/data/artifacts/e4acfac81892/grok.html b/data/artifacts/e4acfac81892/grok.html
new file mode 100644
index 0000000..a8e185b
--- /dev/null
+++ b/data/artifacts/e4acfac81892/grok.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; font-family: sans-serif; font-size: 24px; }
+</style>
+</head>
+<body>
+<p>Blue is a nice color.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e4acfac81892/grok.png b/data/artifacts/e4acfac81892/grok.png
new file mode 100644
index 0000000..355524e
Binary files /dev/null and b/data/artifacts/e4acfac81892/grok.png differ
diff --git a/data/artifacts/e4acfac81892/hermes3-8b.html b/data/artifacts/e4acfac81892/hermes3-8b.html
new file mode 100644
index 0000000..ee1ad27
--- /dev/null
+++ b/data/artifacts/e4acfac81892/hermes3-8b.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Color Challenge</title>
+ <style>
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ font-family: Arial, sans-serif;
+ background-color: #3498db;
+ }
+ h1 {
+ color: white;
+ text-shadow: 2px 2px 0 #333;
+ }
+ </style>
+</head>
+<body>
+ <h1>Blue</h1>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e4acfac81892/hermes3-8b.png b/data/artifacts/e4acfac81892/hermes3-8b.png
new file mode 100644
index 0000000..9bce454
Binary files /dev/null and b/data/artifacts/e4acfac81892/hermes3-8b.png differ
diff --git a/data/artifacts/e4acfac81892/kimi.html b/data/artifacts/e4acfac81892/kimi.html
new file mode 100644
index 0000000..8b27af2
--- /dev/null
+++ b/data/artifacts/e4acfac81892/kimi.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<style>
+body {
+ margin: 0;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-family: system-ui, sans-serif;
+ background: #fafafa;
+}
+p {
+ font-size: 2rem;
+ color: #222;
+}
+</style>
+</head>
+<body>
+<p>Cyan glows softly.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/e4acfac81892/kimi.png b/data/artifacts/e4acfac81892/kimi.png
new file mode 100644
index 0000000..fe2810d
Binary files /dev/null and b/data/artifacts/e4acfac81892/kimi.png differ
diff --git a/data/artifacts/e4acfac81892/qwen25-7b.html b/data/artifacts/e4acfac81892/qwen25-7b.html
new file mode 100644
index 0000000..72cf53b
--- /dev/null
+++ b/data/artifacts/e4acfac81892/qwen25-7b.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html><head><style></style></head><body contenteditable><p>Red</p></body></html>
\ No newline at end of file
diff --git a/data/artifacts/e4acfac81892/qwen25-7b.png b/data/artifacts/e4acfac81892/qwen25-7b.png
new file mode 100644
index 0000000..94d2a29
Binary files /dev/null and b/data/artifacts/e4acfac81892/qwen25-7b.png differ
diff --git a/data/artifacts/e4acfac81892/qwen3-14b.html b/data/artifacts/e4acfac81892/qwen3-14b.html
new file mode 100644
index 0000000..4ceeb97
--- /dev/null
+++ b/data/artifacts/e4acfac81892/qwen3-14b.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html><head><style>body{display:flex;align-items:center;justify-content:center;height:100vh;font-family:sans-serif;}</style></head><body><div style="color:#ff6347;">The color is tomato.</div></body></html>
\ No newline at end of file
diff --git a/data/artifacts/e4acfac81892/qwen3-14b.png b/data/artifacts/e4acfac81892/qwen3-14b.png
new file mode 100644
index 0000000..df58597
Binary files /dev/null and b/data/artifacts/e4acfac81892/qwen3-14b.png differ
diff --git a/data/challenges.json b/data/challenges.json
index 5a8ada4..fcf3d35 100644
--- a/data/challenges.json
+++ b/data/challenges.json
@@ -26,12 +26,14 @@
},
{
"model": "qwen25-7b",
- "status": "error",
- "error": "timeout after 600s",
- "seconds": 600,
- "cost": null,
- "started_at": "2026-07-23T05:26:27.484Z",
- "finished_at": "2026-07-23T05:36:27.494Z"
+ "status": "done",
+ "error": null,
+ "seconds": 16,
+ "cost": 0,
+ "started_at": "2026-07-23T14:12:58.827Z",
+ "finished_at": "2026-07-23T14:13:15.128Z",
+ "bytes": 2275,
+ "thumb": true
},
{
"model": "gemma3-12b",
@@ -103,12 +105,13 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "error",
- "error": "no HTML document in model output (0 chars)",
- "seconds": 142,
+ "status": "queued",
+ "error": null,
+ "seconds": 0,
"cost": null,
- "started_at": "2026-07-23T05:29:23.133Z",
- "finished_at": "2026-07-23T05:31:44.783Z"
+ "started_at": null,
+ "finished_at": null,
+ "queued_at": "2026-07-23T14:14:17.673Z"
},
{
"model": "gemma3-12b",
@@ -133,11 +136,15 @@
},
{
"model": "qwen25-7b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T05:36:27.495Z"
+ "status": "done",
+ "error": null,
+ "seconds": 8,
+ "cost": 0,
+ "started_at": "2026-07-23T14:05:31.075Z",
+ "resumes": 1,
+ "finished_at": "2026-07-23T14:05:39.250Z",
+ "bytes": 1282,
+ "thumb": true
},
{
"model": "claude",
@@ -161,12 +168,14 @@
},
{
"model": "gpt",
- "status": "error",
- "error": "openai error: {\"message\":\"The model `gpt-5.3` does not exist or you do not have access to it.\",\"type\":\"invalid_request_error\",\"param\":null,\"code\":\"model_not_found\"}",
- "seconds": 1,
- "cost": null,
- "started_at": "2026-07-23T05:29:23.130Z",
- "finished_at": "2026-07-23T05:29:24.073Z"
+ "status": "done",
+ "error": null,
+ "seconds": 28,
+ "cost": 0.0367,
+ "started_at": "2026-07-23T14:12:58.855Z",
+ "finished_at": "2026-07-23T14:13:27.235Z",
+ "bytes": 7809,
+ "thumb": true
},
{
"model": "grok",
@@ -181,7 +190,9 @@
}
],
"voted_at": "2026-07-23T05:51:21.751Z",
- "category": "Games"
+ "category": "Games",
+ "aiPick": null,
+ "judging": false
},
{
"id": "74df3b61e7ca",
@@ -192,12 +203,13 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "error",
- "error": "no HTML document in model output (0 chars)",
- "seconds": 137,
+ "status": "queued",
+ "error": null,
+ "seconds": 0,
"cost": null,
- "started_at": "2026-07-23T05:39:50.243Z",
- "finished_at": "2026-07-23T05:42:07.510Z"
+ "started_at": null,
+ "finished_at": null,
+ "queued_at": "2026-07-23T14:14:17.689Z"
},
{
"model": "gemma3-12b",
@@ -229,12 +241,15 @@
},
{
"model": "qwen25-7b",
- "status": "error",
- "error": "timeout after 600s",
- "seconds": 600,
- "cost": null,
- "started_at": "2026-07-23T05:39:54.409Z",
- "finished_at": "2026-07-23T05:49:54.388Z"
+ "status": "done",
+ "error": null,
+ "seconds": 14,
+ "cost": 0,
+ "started_at": "2026-07-23T14:14:17.707Z",
+ "finished_at": "2026-07-23T14:14:31.637Z",
+ "bytes": 2184,
+ "thumb": true,
+ "queued_at": "2026-07-23T14:14:17.704Z"
},
{
"model": "claude",
@@ -302,7 +317,7 @@
],
"voted_at": "2026-07-23T05:51:45.195Z",
"category": "Games",
- "judging": true,
+ "judging": false,
"judged_at": null
},
{
@@ -421,21 +436,25 @@
},
{
"model": "gemma3-12b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T06:28:35.082Z",
- "finished_at": "2026-07-23T06:28:29.518Z"
+ "status": "done",
+ "error": null,
+ "seconds": 86,
+ "cost": 0,
+ "started_at": "2026-07-23T14:05:31.077Z",
+ "finished_at": "2026-07-23T14:06:56.639Z",
+ "resumes": 1,
+ "bytes": 12938,
+ "thumb": true
},
{
"model": "hermes3-8b",
- "status": "error",
- "error": "never started — model host busy/down",
+ "status": "queued",
+ "error": null,
"seconds": 0,
"cost": null,
"started_at": null,
- "finished_at": "2026-07-23T06:28:29.518Z"
+ "finished_at": null,
+ "queued_at": "2026-07-23T14:14:17.719Z"
},
{
"model": "qwen25-7b",
@@ -459,12 +478,15 @@
},
{
"model": "kimi",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T06:28:09.641Z",
- "finished_at": "2026-07-23T06:24:29.510Z"
+ "status": "done",
+ "error": null,
+ "seconds": 223,
+ "cost": 0.0288,
+ "started_at": "2026-07-23T14:05:31.082Z",
+ "finished_at": "2026-07-23T14:09:14.206Z",
+ "resumes": 1,
+ "bytes": 45186,
+ "thumb": true
},
{
"model": "gpt",
@@ -479,15 +501,20 @@
},
{
"model": "grok",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T06:26:33.968Z",
- "finished_at": "2026-07-23T06:24:29.510Z"
+ "status": "done",
+ "error": null,
+ "seconds": 139,
+ "cost": 0.1448,
+ "started_at": "2026-07-23T14:05:31.090Z",
+ "finished_at": "2026-07-23T14:07:50.412Z",
+ "resumes": 1,
+ "bytes": 31207,
+ "thumb": true
}
],
- "category": "Real Work"
+ "category": "Real Work",
+ "aiPick": null,
+ "judging": false
},
{
"id": "35303b0ef3b8",
@@ -498,66 +525,95 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "error",
- "error": "never started — model host busy/down",
+ "status": "queued",
+ "error": null,
"seconds": 0,
"cost": null,
"started_at": null,
- "finished_at": "2026-07-23T06:23:15.481Z"
+ "finished_at": null,
+ "queued_at": "2026-07-23T14:14:17.733Z"
},
{
"model": "gemma3-12b",
- "status": "error",
- "error": "never started — model host busy/down",
+ "status": "queued",
+ "error": null,
"seconds": 0,
"cost": null,
"started_at": null,
- "finished_at": "2026-07-23T06:23:15.481Z"
+ "finished_at": null,
+ "queued_at": "2026-07-23T14:14:17.748Z"
},
{
"model": "hermes3-8b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 4,
+ "cost": 0,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:06:56.640Z",
+ "finished_at": "2026-07-23T14:07:00.435Z",
+ "bytes": 310
},
{
"model": "qwen25-7b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": null,
+ "seconds": 1,
+ "cost": 0,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:05:39.251Z",
+ "finished_at": "2026-07-23T14:05:40.370Z",
+ "bytes": 105,
+ "thumb": true
},
{
"model": "claude",
"status": "error",
"error": "server restarted mid-run",
"seconds": 0,
- "cost": null
+ "cost": null,
+ "resumes": 2
},
{
"model": "kimi",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 20,
+ "cost": 0.0023,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:09:14.209Z",
+ "finished_at": "2026-07-23T14:09:33.862Z",
+ "bytes": 533,
+ "thumb": true
},
{
"model": "gpt",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": null,
+ "seconds": 3,
+ "cost": 0.0014,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:05:31.091Z",
+ "finished_at": "2026-07-23T14:05:34.130Z",
+ "bytes": 298,
+ "thumb": true
},
{
"model": "grok",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 8,
+ "cost": 0.0025,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:07:50.414Z",
+ "finished_at": "2026-07-23T14:07:58.725Z",
+ "bytes": 301,
+ "thumb": true
}
],
- "category": "Custom"
+ "category": "Custom",
+ "aiPick": null,
+ "judging": false
},
{
"id": "8dd797019e83",
@@ -622,62 +678,100 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 18,
+ "cost": 0,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:07:00.437Z",
+ "finished_at": "2026-07-23T14:07:18.609Z",
+ "bytes": 220,
+ "thumb": true
},
{
"model": "gemma3-12b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 11,
+ "cost": 0,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:07:18.611Z",
+ "finished_at": "2026-07-23T14:07:29.792Z",
+ "bytes": 101,
+ "thumb": true
},
{
"model": "hermes3-8b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 4,
+ "cost": 0,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:07:29.795Z",
+ "finished_at": "2026-07-23T14:07:33.576Z",
+ "bytes": 460,
+ "thumb": true
},
{
"model": "qwen25-7b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": null,
+ "seconds": 1,
+ "cost": 0,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:05:40.370Z",
+ "finished_at": "2026-07-23T14:05:41.194Z",
+ "bytes": 95,
+ "thumb": true
},
{
"model": "claude",
"status": "error",
"error": "server restarted mid-run",
"seconds": 0,
- "cost": null
+ "cost": null,
+ "resumes": 2
},
{
"model": "kimi",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 32,
+ "cost": 0.0037,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:09:33.863Z",
+ "finished_at": "2026-07-23T14:10:05.906Z",
+ "bytes": 343,
+ "thumb": true
},
{
"model": "gpt",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": null,
+ "seconds": 17,
+ "cost": 0.0015,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:05:34.131Z",
+ "finished_at": "2026-07-23T14:05:50.985Z",
+ "bytes": 328,
+ "thumb": true
},
{
"model": "grok",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 5,
+ "cost": 0.0021,
+ "resumes": 1,
+ "started_at": "2026-07-23T14:07:58.726Z",
+ "finished_at": "2026-07-23T14:08:03.555Z",
+ "bytes": 244,
+ "thumb": true
}
],
- "category": "Custom"
+ "category": "Custom",
+ "aiPick": null,
+ "judging": false
},
{
"id": "02bc35a3d0b2",
@@ -898,12 +992,13 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "error",
- "error": "no HTML document in model output (0 chars)",
- "seconds": 132,
+ "status": "queued",
+ "error": null,
+ "seconds": 0,
"cost": null,
- "started_at": "2026-07-23T06:41:58.771Z",
- "finished_at": "2026-07-23T06:44:10.361Z"
+ "started_at": null,
+ "finished_at": null,
+ "queued_at": "2026-07-23T14:14:17.764Z"
},
{
"model": "gemma3-12b",
@@ -1087,30 +1182,39 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T13:27:17.303Z",
- "finished_at": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 128,
+ "cost": 0,
+ "started_at": "2026-07-23T14:07:33.578Z",
+ "finished_at": "2026-07-23T14:09:41.093Z",
+ "resumes": 1,
+ "bytes": 2538,
+ "thumb": true
},
{
"model": "gemma3-12b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 36,
+ "cost": 0,
+ "started_at": "2026-07-23T14:09:41.094Z",
+ "finished_at": "2026-07-23T14:10:17.026Z",
+ "resumes": 1,
+ "bytes": 5238,
+ "thumb": true
},
{
"model": "hermes3-8b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 15,
+ "cost": 0,
+ "started_at": "2026-07-23T14:10:17.028Z",
+ "finished_at": "2026-07-23T14:10:31.749Z",
+ "resumes": 1,
+ "bytes": 4541,
+ "thumb": true
},
{
"model": "qwen25-7b",
@@ -1125,12 +1229,15 @@
},
{
"model": "kimi",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T13:27:26.287Z",
- "finished_at": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 155,
+ "cost": 0.019,
+ "started_at": "2026-07-23T14:10:05.908Z",
+ "finished_at": "2026-07-23T14:12:40.783Z",
+ "resumes": 1,
+ "bytes": 9994,
+ "thumb": true
},
{
"model": "gpt",
@@ -1145,14 +1252,19 @@
},
{
"model": "grok",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T13:27:30.361Z",
- "finished_at": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 89,
+ "cost": 0.1112,
+ "started_at": "2026-07-23T14:08:03.558Z",
+ "finished_at": "2026-07-23T14:09:32.772Z",
+ "resumes": 1,
+ "bytes": 17194,
+ "thumb": true
}
- ]
+ ],
+ "aiPick": null,
+ "judging": false
},
{
"id": "f2af82652fcd",
@@ -1239,7 +1351,7 @@
"thumb": true
}
],
- "judging": true,
+ "judging": false,
"voted_at": "2026-07-23T13:35:47.844Z"
},
{
@@ -1303,7 +1415,7 @@
"thumb": true
}
],
- "judging": true,
+ "judging": false,
"category": "Custom"
},
{
@@ -1360,7 +1472,7 @@
"thumb": true
}
],
- "judging": true,
+ "judging": false,
"category": "Games"
},
{
@@ -1373,30 +1485,39 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T13:40:24.963Z",
- "finished_at": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 66,
+ "cost": 0,
+ "started_at": "2026-07-23T14:10:31.751Z",
+ "finished_at": "2026-07-23T14:11:38.182Z",
+ "resumes": 1,
+ "bytes": 3663,
+ "thumb": true
},
{
"model": "gemma3-12b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 25,
+ "cost": 0,
+ "started_at": "2026-07-23T14:11:38.185Z",
+ "finished_at": "2026-07-23T14:12:03.253Z",
+ "resumes": 1,
+ "bytes": 3608,
+ "thumb": true
},
{
"model": "hermes3-8b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 10,
+ "cost": 0,
+ "started_at": "2026-07-23T14:12:03.256Z",
+ "finished_at": "2026-07-23T14:12:13.533Z",
+ "resumes": 1,
+ "bytes": 2819,
+ "thumb": true
},
{
"model": "qwen25-7b",
@@ -1442,7 +1563,9 @@
"bytes": 31773,
"thumb": true
}
- ]
+ ],
+ "aiPick": null,
+ "judging": false
},
{
"id": "be56e35a6067",
@@ -1561,7 +1684,7 @@
"thumb": true
}
],
- "judging": true
+ "judging": false
},
{
"id": "ca355985ca95",
@@ -1617,12 +1740,14 @@
},
{
"model": "kimi",
- "status": "error",
- "error": "server restarted mid-run",
+ "status": "running",
+ "error": null,
"seconds": 0,
"cost": null,
- "started_at": "2026-07-23T13:53:42.040Z",
- "finished_at": null
+ "started_at": "2026-07-23T14:17:42.444Z",
+ "finished_at": null,
+ "resumes": 1,
+ "queued_at": "2026-07-23T14:14:17.779Z"
},
{
"model": "gpt",
@@ -1647,7 +1772,8 @@
"thumb": true
}
],
- "voted_at": "2026-07-23T13:50:02.948Z"
+ "voted_at": "2026-07-23T13:50:02.948Z",
+ "aiPick": null
},
{
"id": "825890cbb1ea",
@@ -1708,14 +1834,19 @@
"runs": [
{
"model": "claude-code",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T13:54:43.224Z",
- "finished_at": null
+ "status": "done",
+ "error": null,
+ "seconds": 16,
+ "cost": 0,
+ "started_at": "2026-07-23T14:05:31.092Z",
+ "finished_at": "2026-07-23T14:05:47.288Z",
+ "resumes": 1,
+ "bytes": 618,
+ "thumb": true
}
- ]
+ ],
+ "aiPick": null,
+ "judging": false
},
{
"id": "d7636ea87ec3",
@@ -1748,7 +1879,7 @@
"thumb": true
}
],
- "judging": true
+ "judging": false
},
{
"id": "34ecbeae4e1f",
@@ -1761,30 +1892,41 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T13:56:13.944Z",
- "finished_at": null
+ "status": "done",
+ "error": null,
+ "seconds": 83,
+ "cost": 0,
+ "started_at": "2026-07-23T14:12:13.536Z",
+ "finished_at": "2026-07-23T14:13:36.671Z",
+ "resumes": 1,
+ "bytes": 8856,
+ "thumb": true
},
{
"model": "gemma3-12b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": null
+ "status": "done",
+ "error": null,
+ "seconds": 71,
+ "cost": 0,
+ "started_at": "2026-07-23T14:14:04.549Z",
+ "finished_at": "2026-07-23T14:15:15.092Z",
+ "resumes": 2,
+ "queued_at": "2026-07-23T14:14:04.543Z",
+ "bytes": 8815,
+ "thumb": true
},
{
"model": "hermes3-8b",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": null
+ "status": "done",
+ "error": null,
+ "seconds": 35,
+ "cost": 0,
+ "started_at": "2026-07-23T14:15:15.094Z",
+ "finished_at": "2026-07-23T14:15:50.591Z",
+ "resumes": 2,
+ "queued_at": "2026-07-23T14:14:04.544Z",
+ "bytes": 7775,
+ "thumb": true
},
{
"model": "qwen25-7b",
@@ -1799,14 +1941,251 @@
},
{
"model": "hf-qwen-coder-32b",
- "status": "error",
- "error": "server restarted mid-run",
+ "status": "done",
+ "error": null,
+ "seconds": 131,
+ "cost": 0,
+ "started_at": "2026-07-23T14:15:50.593Z",
+ "finished_at": "2026-07-23T14:18:01.998Z",
+ "resumes": 2,
+ "queued_at": "2026-07-23T14:14:04.545Z",
+ "bytes": 7922,
+ "thumb": true
+ }
+ ],
+ "category": "Real Work",
+ "aiPick": null,
+ "judging": true
+ },
+ {
+ "id": "75ec2d6d47f7",
+ "title": "Gravity Slingshot Playground",
+ "prompt": "Build a single self-contained HTML file: an interactive gravity playground. Drag to slingshot balls; they collide elastically, leave fading trails, and are pulled by a central mass. Add a few obstacles. Pure canvas + JS, 60fps. Output ONLY the HTML.",
+ "category": "Custom",
+ "created_at": "2026-07-23T13:57:37.041Z",
+ "winner": "gpt",
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "done",
+ "error": null,
+ "seconds": 134,
+ "cost": 0,
+ "started_at": "2026-07-23T13:57:37.045Z",
+ "finished_at": "2026-07-23T13:59:50.722Z",
+ "bytes": 1061,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The playground is interactive and fulfills the requirements but lacks some visual polish.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "done",
+ "error": null,
+ "seconds": 66,
+ "cost": 0,
+ "started_at": "2026-07-23T13:59:50.724Z",
+ "finished_at": "2026-07-23T14:00:57.194Z",
+ "bytes": 7370,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The playground is interactive and fulfills the challenge requirements but lacks visual polish.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "done",
+ "error": null,
+ "seconds": 23,
+ "cost": 0,
+ "started_at": "2026-07-23T14:00:57.195Z",
+ "finished_at": "2026-07-23T14:01:19.735Z",
+ "bytes": 5390,
+ "thumb": true,
+ "aiScore": 5.8,
+ "aiReason": "The model successfully creates an interactive gravity playground but lacks some visual polish and detail.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 4.5
+ },
+ "aiSpread": 2.5
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 20,
+ "cost": 0,
+ "started_at": "2026-07-23T13:57:37.046Z",
+ "finished_at": "2026-07-23T13:57:56.607Z",
+ "bytes": 2795,
+ "thumb": true,
+ "aiScore": 5.8,
+ "aiReason": "The image is blank and does not show any interactive content or visual elements related to the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 4,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 3.5
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 142,
+ "cost": 0.0175,
+ "started_at": "2026-07-23T13:57:37.046Z",
+ "finished_at": "2026-07-23T13:59:59.143Z",
+ "bytes": 7467,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The model fulfills the challenge by creating an interactive gravity playground but lacks some visual polish and detail.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 48,
+ "cost": 0.0686,
+ "started_at": "2026-07-23T13:57:37.054Z",
+ "finished_at": "2026-07-23T13:58:24.626Z",
+ "bytes": 15362,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The game is fully functional and visually appealing, meeting all the criteria of the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 65,
+ "cost": 0.0898,
+ "started_at": "2026-07-23T13:57:37.056Z",
+ "finished_at": "2026-07-23T13:58:42.119Z",
+ "bytes": 14951,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The model fulfills the challenge requirements and has good visual quality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
+ }
+ ],
+ "judging": false,
+ "aiPick": "gpt",
+ "judged_at": "2026-07-23T14:01:56.459Z",
+ "voted_at": "2026-07-23T14:04:47.358Z"
+ },
+ {
+ "id": "e14b05b7754d",
+ "title": "Sorting Race Visualizer",
+ "prompt": "Build a single self-contained HTML file: three sorting algorithms (bubble, quicksort, merge) racing side by side as animated bar charts, each labeled, with a Start/Shuffle button. Color-code comparisons vs swaps. Pure canvas or divs. Output ONLY the HTML.",
+ "category": "Games",
+ "created_at": "2026-07-23T14:04:47.412Z",
+ "winner": null,
+ "runs": [
+ {
+ "model": "qwen3-14b",
+ "status": "running",
+ "error": null,
+ "seconds": 0,
+ "cost": null,
+ "started_at": "2026-07-23T14:18:01.999Z",
+ "finished_at": null,
+ "resumes": 2,
+ "queued_at": "2026-07-23T14:14:04.546Z"
+ },
+ {
+ "model": "gemma3-12b",
+ "status": "queued",
+ "error": null,
+ "seconds": 0,
+ "cost": null,
+ "started_at": null,
+ "finished_at": null,
+ "resumes": 2,
+ "queued_at": "2026-07-23T14:14:04.547Z"
+ },
+ {
+ "model": "hermes3-8b",
+ "status": "queued",
+ "error": null,
"seconds": 0,
"cost": null,
"started_at": null,
- "finished_at": null
+ "finished_at": null,
+ "resumes": 2,
+ "queued_at": "2026-07-23T14:14:04.547Z"
+ },
+ {
+ "model": "qwen25-7b",
+ "status": "done",
+ "error": null,
+ "seconds": 29,
+ "cost": 0,
+ "started_at": "2026-07-23T14:04:47.426Z",
+ "finished_at": "2026-07-23T14:05:16.457Z",
+ "bytes": 4417,
+ "thumb": true
+ },
+ {
+ "model": "kimi",
+ "status": "done",
+ "error": null,
+ "seconds": 218,
+ "cost": 0.0276,
+ "started_at": "2026-07-23T14:14:04.556Z",
+ "finished_at": "2026-07-23T14:17:42.442Z",
+ "resumes": 2,
+ "queued_at": "2026-07-23T14:14:04.548Z",
+ "bytes": 11220,
+ "thumb": true
+ },
+ {
+ "model": "gpt",
+ "status": "done",
+ "error": null,
+ "seconds": 27,
+ "cost": 0.0486,
+ "started_at": "2026-07-23T14:04:47.430Z",
+ "finished_at": "2026-07-23T14:05:14.887Z",
+ "bytes": 14238,
+ "thumb": true
+ },
+ {
+ "model": "grok",
+ "status": "done",
+ "error": null,
+ "seconds": 40,
+ "cost": 0.0571,
+ "started_at": "2026-07-23T14:09:32.774Z",
+ "finished_at": "2026-07-23T14:10:12.357Z",
+ "resumes": 1,
+ "bytes": 10882,
+ "thumb": true
}
],
- "category": "Real Work"
+ "aiPick": null
}
]
\ No newline at end of file
diff --git a/data/costlog.jsonl b/data/costlog.jsonl
index 3933467..ad9d08f 100644
--- a/data/costlog.jsonl
+++ b/data/costlog.jsonl
@@ -28,3 +28,20 @@
{"ts":"2026-07-23T13:48:28.243Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":134,"output_tokens":2253,"cost_usd":0.031776}
{"ts":"2026-07-23T13:48:30.706Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":340,"output_tokens":1992,"cost_usd":0.0309}
{"ts":"2026-07-23T13:54:06.203Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":134,"output_tokens":2036,"cost_usd":0.028739}
+{"ts":"2026-07-23T13:58:24.622Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":137,"output_tokens":4886,"cost_usd":0.068644}
+{"ts":"2026-07-23T13:58:42.115Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":341,"output_tokens":5919,"cost_usd":0.089808}
+{"ts":"2026-07-23T13:59:59.139Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":138,"output_tokens":6980,"cost_usd":0.017533}
+{"ts":"2026-07-23T14:05:14.884Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":136,"output_tokens":3452,"cost_usd":0.048566}
+{"ts":"2026-07-23T14:05:34.128Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":92,"output_tokens":91,"cost_usd":0.001435}
+{"ts":"2026-07-23T14:05:50.982Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":92,"output_tokens":97,"cost_usd":0.001519}
+{"ts":"2026-07-23T14:07:50.407Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":762,"output_tokens":9500,"cost_usd":0.144786}
+{"ts":"2026-07-23T14:07:58.724Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":296,"output_tokens":108,"cost_usd":0.002508}
+{"ts":"2026-07-23T14:08:03.552Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":296,"output_tokens":80,"cost_usd":0.002088}
+{"ts":"2026-07-23T14:09:14.198Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":559,"output_tokens":11388,"cost_usd":0.028805}
+{"ts":"2026-07-23T14:09:32.766Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":323,"output_tokens":7350,"cost_usd":0.111219}
+{"ts":"2026-07-23T14:09:33.861Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":93,"output_tokens":895,"cost_usd":0.002293}
+{"ts":"2026-07-23T14:10:05.902Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":93,"output_tokens":1463,"cost_usd":0.003713}
+{"ts":"2026-07-23T14:10:12.355Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":342,"output_tokens":3738,"cost_usd":0.057096}
+{"ts":"2026-07-23T14:12:40.777Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":120,"output_tokens":7558,"cost_usd":0.018967}
+{"ts":"2026-07-23T14:13:27.232Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":88,"output_tokens":2610,"cost_usd":0.036694}
+{"ts":"2026-07-23T14:17:42.438Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":137,"output_tokens":11001,"cost_usd":0.027585}
diff --git a/data/votes.jsonl b/data/votes.jsonl
index 145a347..b437bf0 100644
--- a/data/votes.jsonl
+++ b/data/votes.jsonl
@@ -6,3 +6,4 @@
{"ts":"2026-07-23T13:10:47.373Z","challenge":"08d1c975f6db","title":"Daily: Bouncing Balls","winner":"gemma3-12b","field":["gemma3-12b","hermes3-8b","qwen25-7b"]}
{"ts":"2026-07-23T13:35:47.844Z","challenge":"f2af82652fcd","title":"california real estate brokers","winner":"grok","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","kimi","gpt","grok"]}
{"ts":"2026-07-23T13:50:02.948Z","challenge":"ca355985ca95","title":"Particle Text Reactor","winner":"qwen25-7b","field":["qwen3-14b","qwen25-7b","gpt","grok"]}
+{"ts":"2026-07-23T14:04:47.358Z","challenge":"75ec2d6d47f7","title":"Gravity Slingshot Playground","winner":"gpt","field":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","kimi","gpt","grok"]}
diff --git a/idea-run/create-cycle.sh b/idea-run/create-cycle.sh
index 2ef3db5..dc68810 100755
--- a/idea-run/create-cycle.sh
+++ b/idea-run/create-cycle.sh
@@ -7,7 +7,7 @@ TITLE="${LINE%%|*}"; PROMPT="${LINE#*|}"
python3 - "$TITLE" "$PROMPT" > /tmp/cyc-payload.json <<'PY'
import json,sys
json.dump({"title":sys.argv[1],"prompt":sys.argv[2],
- "models":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","kimi","gpt","grok"]},
+ "models":["qwen3-14b","gemma3-12b","hermes3-8b","qwen25-7b","claude-code","kimi","gpt","grok"]},
sys.stdout)
PY
curl -s -m 20 -u admin:DW2024! -X POST http://127.0.0.1:9758/api/challenges \
diff --git a/idea-run/harvest.md b/idea-run/harvest.md
index 5f5b36d..0b6c55f 100644
--- a/idea-run/harvest.md
+++ b/idea-run/harvest.md
@@ -3,3 +3,32 @@ Working models: qwen3-14b, gemma3-12b, hermes3-8b, qwen25-7b (local $0) · kimi
Claude EXCLUDED — Anthropic key out of credit (billing, Steve to fund).
keep-alive now excludes model-arena so batches don't get killed mid-run.
+## Cycle 1 — Particle Text Reactor (ca355985ca95)
+Done: gpt(8KB), grok(5KB), qwen25(3KB), qwen3(2.6KB) · Errored: gemma/hermes/kimi (server-restart, pre-fix)
+Contrarian verdict — RANKED:
+1. **GPT** — only complete+correct impl: offscreen-canvas getImageData text sampling w/ dynamic gap, delta-time physics, DPI-aware, additive glow, working click cycle. (minor pts[0] dup bug)
+2. **Grok** — cleaner 4-state machine, per-particle spring+friction, Fisher-Yates target shuffle, two-layer render (halo+core). No DPI; targets array bloated; hold-shimmer drifts.
+3. qwen25 — WRONG BRIEF: particles + fillText("ARENA") unrelated; dup animation loop on click degrades fps.
+4. qwen3 — TRUNCATED at line 100, syntax error, won't run; bounding-box placement would've made rectangles not glyphs.
+
+**Best idea per model harvested:**
+- gpt: getImageData offscreen sampling + dynamic gap = the only way particles land IN letter strokes; delta-time physics.
+- grok: Fisher-Yates shuffle of target assignments (reform looks different each cycle) + halo-core two-layer glow.
+- qwen25: (nothing reusable) — cautionary dup-loop bug.
+- qwen3: named duration constants for readability.
+
+HIGHEST-LEVERAGE across all: GPT's getImageData dynamic-gap sampling + grok's target shuffle = the correct foundation; everything else is tuning.
+
+## Cycle 2 — Gravity Slingshot Playground (75ec2d6d47f7) · ALL 7 DONE (restart fix held)
+Contrarian RANKED:
+1. **GPT** (15KB) — all 6 features correct: drag-slingshot(+drag existing balls), 2D elastic via normal/tangent decomp, softened inverse-sq gravity, per-segment trail fade, AABB obstacles. Weakness: O(n) trail draw calls.
+2. **Grok** (15KB) — impulse-based elastic (cleaner), circles+ROTATED-rect obstacles (best), DPR, sparks. Trail "fade" is the ambient-wipe cheat; hardcoded dt=0.016.
+3. **Kimi** (7.5KB, reasoning) — ALL 6 features correct at HALF the size; no fluff, central-mass singularity bounce guard. Punched above its weight — the reasoning-model trait: got the core right, didn't pad with broken extras.
+4. Hermes8b — slingshot wrong (fixed catapult), ball-ball collision never called, gravity REPELS.
+5. Gemma12b — fixed catapult, Trail class crashes (this.color undefined) + memory leak, non-reflective obstacle bounce.
+6. Qwen25-7b — double gravity (one repels), mutates event objects, no release handler, no obstacles.
+7. Qwen3-14b — STUB, cut off at line 43 "// Initialize obstacles". Blank canvas.
+
+**Best idea per model:** gpt=per-segment trail fade (alpha=t/len + tapered width) · grok=impulse collision j=-(1+e)*dvn/(1/ma+1/mb) + approaching-only guard + rgba-wipe ambient trails · kimi=central-mass singularity guard (dist<40 → elastic bounce vs divide-by-near-zero).
+HIGHEST-LEVERAGE: Grok's impulse elastic-collision formula = the pass/fail gate (every model that nailed it worked; every one that hand-waved it broke).
+COMPOSITE the contrarian proposed: Grok impulse+rotated-rect+DPR+sparks ⊕ GPT trail-fade+drag-slingshot ⊕ Kimi singularity-guard = beats all 7.
← ca4eed6 watchdog: age queued runs from their own queued_at, not chal
·
back to Model Arena
·
design tools for arena models: figma / opendesign / hyperfra 7df2798 →