← back to Model Arena
yolo idea 1: artifact auto-screenshots — headless-render each artifact to a thumbnail; visual card strip + click-to-run-live poster panes; startup backfill + /thumb route
37d6993fd5fbca7338030dc3fca3696cf98005fa · 2026-07-22 23:29:37 -0700 · Steve
Files touched
A data/artifacts/0ac1af2eaed4/gemma3-12b.pngA data/artifacts/0ac1af2eaed4/hermes3-8b.pngA data/artifacts/0ac1af2eaed4/kimi.pngA data/artifacts/0ac1af2eaed4/qwen3-14b.pngA data/artifacts/74df3b61e7ca/gemma3-12b.pngA data/artifacts/93946ee6d736/gemma3-12b.htmlA data/artifacts/93946ee6d736/gpt.htmlA data/artifacts/93946ee6d736/grok.htmlA data/artifacts/93946ee6d736/hermes3-8b.htmlA data/artifacts/93946ee6d736/kimi.htmlA data/artifacts/93946ee6d736/qwen25-7b.htmlA data/artifacts/93946ee6d736/qwen3-14b.htmlA data/artifacts/966d54f224fd/grok.pngA data/artifacts/966d54f224fd/hermes3-8b.pngA data/artifacts/966d54f224fd/kimi.pngA data/artifacts/acebf42d306a/gpt.htmlA data/artifacts/acebf42d306a/qwen25-7b.htmlM data/artifacts/acebf42d306a/qwen3-14b.htmlM data/challenges.jsonM data/costlog.jsonlM public/index.htmlM server.jsA yolo/IDEAS.md
Diff
commit 37d6993fd5fbca7338030dc3fca3696cf98005fa
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jul 22 23:29:37 2026 -0700
yolo idea 1: artifact auto-screenshots — headless-render each artifact to a thumbnail; visual card strip + click-to-run-live poster panes; startup backfill + /thumb route
---
data/artifacts/0ac1af2eaed4/gemma3-12b.png | Bin 0 -> 2724 bytes
data/artifacts/0ac1af2eaed4/hermes3-8b.png | Bin 0 -> 5857 bytes
data/artifacts/0ac1af2eaed4/kimi.png | Bin 0 -> 12044 bytes
data/artifacts/0ac1af2eaed4/qwen3-14b.png | Bin 0 -> 2822 bytes
data/artifacts/74df3b61e7ca/gemma3-12b.png | Bin 0 -> 47440 bytes
data/artifacts/93946ee6d736/gemma3-12b.html | 15 +
data/artifacts/93946ee6d736/gpt.html | 688 +++++++++++++++++++++++++++
data/artifacts/93946ee6d736/grok.html | 706 ++++++++++++++++++++++++++++
data/artifacts/93946ee6d736/hermes3-8b.html | 67 +++
data/artifacts/93946ee6d736/kimi.html | 466 ++++++++++++++++++
data/artifacts/93946ee6d736/qwen25-7b.html | 63 +++
data/artifacts/93946ee6d736/qwen3-14b.html | 34 ++
data/artifacts/966d54f224fd/grok.png | Bin 0 -> 39726 bytes
data/artifacts/966d54f224fd/hermes3-8b.png | Bin 0 -> 5450 bytes
data/artifacts/966d54f224fd/kimi.png | Bin 0 -> 16653 bytes
data/artifacts/acebf42d306a/gpt.html | 532 +++++++++++++++++++++
data/artifacts/acebf42d306a/qwen25-7b.html | 84 ++++
data/artifacts/acebf42d306a/qwen3-14b.html | 92 ++--
data/challenges.json | 173 ++++---
data/costlog.jsonl | 4 +
public/index.html | 30 +-
server.js | 41 +-
yolo/IDEAS.md | 16 +
23 files changed, 2885 insertions(+), 126 deletions(-)
diff --git a/data/artifacts/0ac1af2eaed4/gemma3-12b.png b/data/artifacts/0ac1af2eaed4/gemma3-12b.png
new file mode 100644
index 0000000..ad57eb4
Binary files /dev/null and b/data/artifacts/0ac1af2eaed4/gemma3-12b.png differ
diff --git a/data/artifacts/0ac1af2eaed4/hermes3-8b.png b/data/artifacts/0ac1af2eaed4/hermes3-8b.png
new file mode 100644
index 0000000..af6c8b6
Binary files /dev/null and b/data/artifacts/0ac1af2eaed4/hermes3-8b.png differ
diff --git a/data/artifacts/0ac1af2eaed4/kimi.png b/data/artifacts/0ac1af2eaed4/kimi.png
new file mode 100644
index 0000000..1dc8e29
Binary files /dev/null and b/data/artifacts/0ac1af2eaed4/kimi.png differ
diff --git a/data/artifacts/0ac1af2eaed4/qwen3-14b.png b/data/artifacts/0ac1af2eaed4/qwen3-14b.png
new file mode 100644
index 0000000..6a0ffb4
Binary files /dev/null and b/data/artifacts/0ac1af2eaed4/qwen3-14b.png differ
diff --git a/data/artifacts/74df3b61e7ca/gemma3-12b.png b/data/artifacts/74df3b61e7ca/gemma3-12b.png
new file mode 100644
index 0000000..45d243a
Binary files /dev/null and b/data/artifacts/74df3b61e7ca/gemma3-12b.png differ
diff --git a/data/artifacts/93946ee6d736/gemma3-12b.html b/data/artifacts/93946ee6d736/gemma3-12b.html
new file mode 100644
index 0000000..50af785
--- /dev/null
+++ b/data/artifacts/93946ee6d736/gemma3-12b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Luxury Residential Space Visualization</title>
+<style>
+body { margin: 0; overflow: hidden; }
+#container { width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; background-color: #f2f2f2; }
+img { max-width: 80%; max-height: 80%; object-fit: contain; border: none; }
+#info { position: absolute; bottom: 10px; left: 10px; background-color: rgba(255, 255, 255, 0.8); padding: 10px; font-family: sans-serif; }
+</style>
+</head>
+<body>
+
+<div id="container">
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPgAAABMCAYAAAC1eX9vAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABlSURBVFjD7dCtTsEgDIU/h5R0iE2T+F+G3+Lz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9uLz9
\ No newline at end of file
diff --git a/data/artifacts/93946ee6d736/gpt.html b/data/artifacts/93946ee6d736/gpt.html
new file mode 100644
index 0000000..9de45be
--- /dev/null
+++ b/data/artifacts/93946ee6d736/gpt.html
@@ -0,0 +1,688 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<title>Luxury Room Wallpaper Visualization</title>
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ background: #111;
+ height: 100%;
+ overflow: hidden;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ color: #111;
+ }
+ canvas {
+ display: block;
+ width: 100vw;
+ height: 72vh;
+ background: #000;
+ }
+ .info {
+ box-sizing: border-box;
+ padding: 18px 22px 26px;
+ background: #faf7f3;
+ color: #222;
+ font-size: 14px;
+ line-height: 1.5;
+ border-top: 1px solid rgba(0,0,0,0.08);
+ }
+ .info h1 {
+ font-size: 18px;
+ margin: 0 0 6px;
+ font-weight: 600;
+ letter-spacing: 0.03em;
+ text-transform: uppercase;
+ }
+ .info h2 {
+ font-size: 14px;
+ margin: 10px 0 4px;
+ font-weight: 600;
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
+ }
+ .info a {
+ color: #3b5ea9;
+ text-decoration: none;
+ }
+ .info a:hover {
+ text-decoration: underline;
+ }
+ .meta-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 18px;
+ margin: 4px 0 8px;
+ font-size: 13px;
+ }
+ .meta-label {
+ font-weight: 600;
+ }
+ .reasoning {
+ margin-top: 6px;
+ font-size: 13px;
+ max-width: 840px;
+ }
+</style>
+</head>
+<body>
+<canvas id="c"></canvas>
+<div class="info">
+ <h1>Applied Wallpaper</h1>
+ <div class="meta-row">
+ <div><span class="meta-label">Name:</span> Candice Olson Botanical Dream Wallpaper COD0199 by York</div>
+ </div>
+ <div class="meta-row">
+ <div><span class="meta-label">Brand / Manufacturer:</span> Candice Olson for York Wallcoverings – Designer Wallcoverings & Fabrics</div>
+ </div>
+ <div class="meta-row">
+ <div><span class="meta-label">Product Link:</span>
+ <a href="https://www.designerwallcoverings.com/products/candice-olson-botanical-dream-wallpaper-cod0199-by-york" target="_blank">
+ https://www.designerwallcoverings.com/products/candice-olson-botanical-dream-wallpaper-cod0199-by-york
+ </a>
+ </div>
+ </div>
+ <h2>Design Rationale</h2>
+ <div class="reasoning">
+ This large-scale botanical with soft, layered neutrals delivers a quietly dramatic envelope that feels luxurious yet serene when wrapped continuously around a room. The painterly foliage, subtle sheen, and organic vertical movement visually lift the ceiling and complement sophisticated furnishings without competing with them, making it well suited for an upscale residential interior.
+ </div>
+</div>
+
+<script>
+(function() {
+ const canvas = document.getElementById('c');
+ const ctx = canvas.getContext('2d');
+
+ function resize() {
+ const ratio = window.devicePixelRatio || 1;
+ canvas.width = canvas.clientWidth * ratio;
+ canvas.height = canvas.clientHeight * ratio;
+ ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
+ draw();
+ }
+
+ window.addEventListener('resize', resize);
+
+ // Simple PRNG for deterministic texture noise
+ let seed = 42;
+ function rand() {
+ seed = (seed * 16807) % 2147483647;
+ return (seed - 1) / 2147483646;
+ }
+
+ // Create an offscreen canvas with a seamless wallpaper repeat-like pattern
+ function createWallpaperPattern() {
+ const off = document.createElement('canvas');
+ const s = 360; // repeat size in pixels (represents ~27" width)
+ off.width = s;
+ off.height = s;
+ const octx = off.getContext('2d');
+
+ // Base ground color - warm parchment
+ const baseGrad = octx.createLinearGradient(0, 0, 0, s);
+ baseGrad.addColorStop(0, '#f5f0e7');
+ baseGrad.addColorStop(1, '#eee3d4');
+ octx.fillStyle = baseGrad;
+ octx.fillRect(0, 0, s, s);
+
+ // Subtle vertical strié for woven feel
+ for (let x = 0; x < s; x += 1) {
+ const alpha = 0.02 + 0.03 * rand();
+ octx.strokeStyle = `rgba(190,170,140,${alpha})`;
+ octx.beginPath();
+ octx.moveTo(x + rand() * 0.3, 0);
+ octx.lineTo(x + rand() * 0.3, s);
+ octx.stroke();
+ }
+
+ // Soft clouding / brushed areas
+ for (let i = 0; i < 1200; i++) {
+ const x = rand() * s;
+ const y = rand() * s;
+ const r = 40 + rand() * 70;
+ const g = octx.createRadialGradient(x, y, 0, x, y, r);
+ const a = 0.03 + rand() * 0.04;
+ g.addColorStop(0, `rgba(255,255,255,${a})`);
+ g.addColorStop(1, 'rgba(255,255,255,0)');
+ octx.fillStyle = g;
+ octx.beginPath();
+ octx.arc(x, y, r, 0, Math.PI * 2);
+ octx.fill();
+ }
+
+ // Function to draw a stylized leaf cluster
+ function drawLeafCluster(cx, cy, scale, rotDeg, toneShift) {
+ octx.save();
+ octx.translate(cx, cy);
+ octx.rotate(rotDeg * Math.PI / 180);
+ octx.scale(scale, scale);
+
+ // Stem
+ octx.strokeStyle = `rgba(${165 + toneShift}, ${150 + toneShift}, ${130 + toneShift}, 0.8)`;
+ octx.lineWidth = 1.4;
+ octx.beginPath();
+ octx.moveTo(0, 40);
+ octx.quadraticCurveTo(-6, 0, 0, -54);
+ octx.stroke();
+
+ // Leaf drawing helper
+ function leaf(x, y, len, width, angle, fill, highlight) {
+ octx.save();
+ octx.translate(x, y);
+ octx.rotate(angle * Math.PI / 180);
+ const grad = octx.createLinearGradient(-width, -len, width, len);
+ grad.addColorStop(0, fill[0]);
+ grad.addColorStop(0.6, fill[1]);
+ grad.addColorStop(1, fill[2]);
+ octx.fillStyle = grad;
+ octx.beginPath();
+ octx.moveTo(0, 0);
+ octx.quadraticCurveTo(width, -len * 0.35, 0, -len);
+ octx.quadraticCurveTo(-width, -len * 0.35, 0, 0);
+ octx.closePath();
+ octx.fill();
+ // Midrib
+ octx.strokeStyle = highlight;
+ octx.lineWidth = 0.7;
+ octx.beginPath();
+ octx.moveTo(0, 0);
+ octx.lineTo(0, -len * 0.98);
+ octx.stroke();
+ octx.restore();
+ }
+
+ const leafBaseFill = [
+ `rgba(${203 + toneShift}, ${193 + toneShift}, ${177 + toneShift}, 0.96)`,
+ `rgba(${190 + toneShift}, ${178 + toneShift}, ${160 + toneShift}, 0.98)`,
+ `rgba(${176 + toneShift}, ${165 + toneShift}, ${147 + toneShift}, 1)`
+ ];
+ const leafLightFill = [
+ `rgba(${216 + toneShift}, ${208 + toneShift}, ${196 + toneShift}, 0.98)`,
+ `rgba(${205 + toneShift}, ${195 + toneShift}, ${179 + toneShift}, 0.99)`,
+ `rgba(${192 + toneShift}, ${180 + toneShift}, ${162 + toneShift}, 1)`
+ ];
+ const highlight = `rgba(255,255,255,0.45)`;
+
+ // Central leaf
+ leaf(0, 0, 65, 20, 0, leafBaseFill, highlight);
+ // Side leaves
+ leaf(-10, -8, 55, 18, -32, leafLightFill, highlight);
+ leaf(10, -6, 58, 18, 30, leafLightFill, highlight);
+ leaf(-6, 16, 48, 16, -55, leafBaseFill, highlight);
+ leaf(8, 14, 45, 15, 58, leafBaseFill, highlight);
+
+ octx.restore();
+ }
+
+ // Tile clusters in a way that will be reasonably seamless
+ const clusters = [
+ {x: s*0.18, y: s*0.80, scale: 1.0, rot: -12, tone: -4},
+ {x: s*0.52, y: s*0.72, scale: 1.18, rot: 8, tone: 0},
+ {x: s*0.86, y: s*0.76, scale: 1.0, rot: 18, tone: -6},
+ {x: s*0.32, y: s*0.38, scale: 1.1, rot: -22, tone: 4},
+ {x: s*0.68, y: s*0.34, scale: 1.05, rot: 15, tone: -2},
+ {x: s*0.12, y: s*0.24, scale: 0.9, rot: -35, tone: 3},
+ {x: s*0.94, y: s*0.24, scale: 0.93, rot: 28, tone: 2}
+ ];
+ clusters.forEach(c => drawLeafCluster(c.x, c.y, c.scale, c.rot, c.tone));
+
+ // Soft shadow to give some depth
+ for (let i = 0; i < 350; i++) {
+ const x = rand() * s;
+ const y = rand() * s;
+ const r = 16 + rand() * 34;
+ const g2 = octx.createRadialGradient(x, y, 0, x, y, r);
+ const a2 = 0.02 + rand() * 0.03;
+ g2.addColorStop(0, `rgba(150,135,120,${a2})`);
+ g2.addColorStop(1, 'rgba(150,135,120,0)');
+ octx.fillStyle = g2;
+ octx.beginPath();
+ octx.arc(x, y, r, 0, Math.PI * 2);
+ octx.fill();
+ }
+
+ // Very fine noise for paper texture
+ const imgData = octx.getImageData(0, 0, s, s);
+ const d = imgData.data;
+ for (let i = 0; i < d.length; i += 4) {
+ const n = (rand() - 0.5) * 10;
+ d[i] = Math.min(255, Math.max(0, d[i] + n));
+ d[i+1] = Math.min(255, Math.max(0, d[i+1] + n));
+ d[i+2] = Math.min(255, Math.max(0, d[i+2] + n));
+ }
+ octx.putImageData(imgData, 0, 0);
+
+ return ctx.createPattern(off, 'repeat');
+ }
+
+ const wallpaperPattern = createWallpaperPattern();
+
+ function drawRectWithPattern(x, y, w, h, transformCallback, shadeColor, shadeAlpha) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(x, y, w, h);
+ ctx.clip();
+
+ ctx.save();
+ if (transformCallback) transformCallback();
+ ctx.fillStyle = wallpaperPattern;
+ ctx.fillRect(x - 500, y - 500, w + 1000, h + 1000);
+ ctx.restore();
+
+ if (shadeAlpha > 0) {
+ ctx.fillStyle = `rgba(${shadeColor.r},${shadeColor.g},${shadeColor.b},${shadeAlpha})`;
+ ctx.fillRect(x, y, w, h);
+ }
+ ctx.restore();
+ }
+
+ function draw() {
+ const w = canvas.clientWidth;
+ const h = canvas.clientHeight;
+ ctx.clearRect(0, 0, w, h);
+
+ // Basic scene proportions
+ const floorHorizon = h * 0.64;
+ const ceiling = h * 0.18;
+ const roomLeft = w * 0.08;
+ const roomRight = w * 0.92;
+ const roomCenter = w * 0.5;
+
+ // Perspective factors
+ const depth = roomCenter - roomLeft;
+ const leftVanishX = roomLeft - depth * 2.0;
+ const rightVanishX = roomRight + depth * 2.0;
+ const vanishY = ceiling - h * 0.35;
+
+ // Draw ceiling
+ ctx.save();
+ const ceilingGrad = ctx.createLinearGradient(0, ceiling, 0, ceiling - h*0.18);
+ ceilingGrad.addColorStop(0, '#f7f2ea');
+ ceilingGrad.addColorStop(1, '#fefbf7');
+ ctx.fillStyle = ceilingGrad;
+ ctx.beginPath();
+ ctx.moveTo(roomLeft, ceiling);
+ ctx.lineTo(roomRight, ceiling);
+ ctx.lineTo(roomRight + depth*0.4, ceiling - h*0.16);
+ ctx.lineTo(roomLeft - depth*0.4, ceiling - h*0.16);
+ ctx.closePath();
+ ctx.fill();
+ ctx.restore();
+
+ // Calculate wall shapes
+ const leftWallTopLeft = {x: roomLeft, y: ceiling};
+ const leftWallTopRight = {x: roomCenter, y: ceiling};
+ const leftWallBottomLeft = {x: roomLeft + depth*0.14, y: floorHorizon};
+ const leftWallBottomRight = {x: roomCenter - depth*0.14, y: floorHorizon};
+
+ const rightWallTopLeft = {x: roomCenter, y: ceiling};
+ const rightWallTopRight = {x: roomRight, y: ceiling};
+ const rightWallBottomLeft = {x: roomCenter + depth*0.14, y: floorHorizon};
+ const rightWallBottomRight = {x: roomRight - depth*0.14, y: floorHorizon};
+
+ // We'll map pattern in screen space but give slight skew to mimic perspective
+
+ // BACK WALL
+ (function() {
+ const x = leftWallTopRight.x;
+ const y = ceiling;
+ const wRect = rightWallTopLeft.x - leftWallTopRight.x;
+ const hRect = floorHorizon - ceiling;
+ drawRectWithPattern(
+ x, y, wRect, hRect,
+ function() {
+ ctx.transform(1.0, 0, 0, 1.02, 0, 0);
+ },
+ {r: 180, g: 170, b: 150},
+ 0.10
+ );
+ })();
+
+ // LEFT WALL
+ (function() {
+ const minX = leftWallTopLeft.x;
+ const maxX = leftWallTopRight.x;
+ const minY = ceiling;
+ const maxY = floorHorizon;
+ drawRectWithPattern(
+ minX, minY, maxX - minX, maxY - minY,
+ function() {
+ const skewX = -0.22;
+ ctx.transform(1, 0, skewX, 1.03, 0, 0);
+ },
+ {r: 120, g: 110, b: 95},
+ 0.25
+ );
+
+ // Clip to trapezoid
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(leftWallTopLeft.x, leftWallTopLeft.y);
+ ctx.lineTo(leftWallTopRight.x, leftWallTopRight.y);
+ ctx.lineTo(leftWallBottomRight.x, leftWallBottomRight.y);
+ ctx.lineTo(leftWallBottomLeft.x, leftWallBottomLeft.y);
+ ctx.closePath();
+ ctx.clip();
+ // Overdraw edges to clean
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = '#000';
+ ctx.fillRect(minX - 5, minY - 5, (maxX - minX) + 10, (maxY - minY) + 10);
+ ctx.restore();
+ })();
+
+ // RIGHT WALL
+ (function() {
+ const minX = rightWallTopLeft.x;
+ const maxX = rightWallTopRight.x;
+ const minY = ceiling;
+ const maxY = floorHorizon;
+ drawRectWithPattern(
+ minX, minY, maxX - minX, maxY - minY,
+ function() {
+ const skewX = 0.22;
+ ctx.transform(1, 0, skewX, 1.03, 0, 0);
+ },
+ {r: 80, g: 72, b: 60},
+ 0.30
+ );
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(rightWallTopLeft.x, rightWallTopLeft.y);
+ ctx.lineTo(rightWallTopRight.x, rightWallTopRight.y);
+ ctx.lineTo(rightWallBottomRight.x, rightWallBottomRight.y);
+ ctx.lineTo(rightWallBottomLeft.x, rightWallBottomLeft.y);
+ ctx.closePath();
+ ctx.clip();
+ ctx.globalCompositeOperation = 'destination-in';
+ ctx.fillStyle = '#000';
+ ctx.fillRect(minX - 5, minY - 5, (maxX - minX) + 10, (maxY - minY) + 10);
+ ctx.restore();
+ })();
+
+ // Corner shadow between walls
+ ctx.save();
+ const cornerGrad = ctx.createLinearGradient(roomCenter - 10, ceiling, roomCenter + 10, ceiling);
+ cornerGrad.addColorStop(0, 'rgba(0,0,0,0.32)');
+ cornerGrad.addColorStop(0.42, 'rgba(0,0,0,0.06)');
+ cornerGrad.addColorStop(0.58, 'rgba(0,0,0,0.06)');
+ cornerGrad.addColorStop(1, 'rgba(0,0,0,0.38)');
+ ctx.fillStyle = cornerGrad;
+ ctx.fillRect(roomCenter - 12, ceiling, 24, floorHorizon - ceiling);
+ ctx.restore();
+
+ // Floor
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(leftWallBottomLeft.x, leftWallBottomLeft.y);
+ ctx.lineTo(rightWallBottomRight.x, rightWallBottomRight.y);
+ ctx.lineTo(rightWallBottomRight.x + depth*0.5, h);
+ ctx.lineTo(leftWallBottomLeft.x - depth*0.5, h);
+ ctx.closePath();
+ const floorGrad = ctx.createLinearGradient(0, floorHorizon, 0, h);
+ floorGrad.addColorStop(0, '#c5b29b');
+ floorGrad.addColorStop(1, '#9c8367');
+ ctx.fillStyle = floorGrad;
+ ctx.fill();
+
+ // Wood planks
+ const numPlanks = 18;
+ for (let i = 0; i <= numPlanks; i++) {
+ const t = i / numPlanks;
+ const x1 = leftWallBottomLeft.x + (rightWallBottomRight.x - leftWallBottomLeft.x) * t;
+ const y1 = leftWallBottomLeft.y;
+ const x2 = (leftWallBottomLeft.x - depth*0.5) + ((rightWallBottomRight.x + depth*0.5) - (leftWallBottomLeft.x - depth*0.5)) * t;
+ const y2 = h;
+ ctx.strokeStyle = 'rgba(90,70,50,0.45)';
+ ctx.lineWidth = 0.8;
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+ }
+ ctx.restore();
+
+ // Baseboards - maintain original trim, not wallpaper
+ ctx.save();
+ ctx.strokeStyle = '#f7f3ec';
+ ctx.lineWidth = 14;
+ ctx.lineCap = 'butt';
+ ctx.beginPath();
+ ctx.moveTo(leftWallBottomLeft.x, leftWallBottomLeft.y - 4);
+ ctx.lineTo(rightWallBottomRight.x, rightWallBottomRight.y - 4);
+ ctx.stroke();
+ ctx.restore();
+
+ // Ceiling molding - original white trim
+ ctx.save();
+ ctx.strokeStyle = '#f8f4ee';
+ ctx.lineWidth = 10;
+ ctx.beginPath();
+ ctx.moveTo(roomLeft, ceiling + 1);
+ ctx.lineTo(roomRight, ceiling + 1);
+ ctx.stroke();
+ ctx.restore();
+
+ // Window on back wall (unchanged architecture)
+ const winW = w * 0.22;
+ const winH = h * 0.34;
+ const winX = roomCenter - winW / 2;
+ const winY = ceiling + (floorHorizon - ceiling) * 0.10;
+ ctx.save();
+
+ // Trim
+ ctx.fillStyle = '#f7f4ee';
+ ctx.fillRect(winX - 8, winY - 8, winW + 16, winH + 16);
+
+ // Glass
+ const glassGrad = ctx.createLinearGradient(winX, winY, winX, winY + winH);
+ glassGrad.addColorStop(0, 'rgba(225,234,243,0.98)');
+ glassGrad.addColorStop(0.5, 'rgba(196,214,228,0.95)');
+ glassGrad.addColorStop(1, 'rgba(170,194,212,0.98)');
+ ctx.fillStyle = glassGrad;
+ ctx.fillRect(winX, winY, winW, winH);
+
+ // Window mullions
+ ctx.strokeStyle = 'rgba(255,255,255,0.85)';
+ ctx.lineWidth = 4;
+ ctx.beginPath();
+ ctx.moveTo(winX + winW / 2, winY);
+ ctx.lineTo(winX + winW / 2, winY + winH);
+ ctx.moveTo(winX, winY + winH / 2);
+ ctx.lineTo(winX + winW, winY + winH / 2);
+ ctx.stroke();
+
+ // Exterior hint (trees)
+ for (let i = 0; i < 20; i++) {
+ const tx = winX + rand() * winW;
+ const ty = winY + winH * (0.45 + rand() * 0.4);
+ const r = 24 + rand() * 30;
+ const gTree = ctx.createRadialGradient(tx, ty, 0, tx, ty, r);
+ gTree.addColorStop(0, 'rgba(122,145,115,0.6)');
+ gTree.addColorStop(1, 'rgba(90,115,100,0)');
+ ctx.fillStyle = gTree;
+ ctx.beginPath();
+ ctx.arc(tx, ty, r, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ // Subtle reflection on glass
+ ctx.globalCompositeOperation = 'lighter';
+ const reflGrad = ctx.createLinearGradient(winX, winY, winX + winW, winY + winH);
+ reflGrad.addColorStop(0, 'rgba(255,255,255,0.12)');
+ reflGrad.addColorStop(0.4, 'rgba(255,255,255,0.02)');
+ reflGrad.addColorStop(1, 'rgba(255,255,255,0)');
+ ctx.fillStyle = reflGrad;
+ ctx.fillRect(winX, winY, winW, winH);
+ ctx.restore();
+
+ // Furniture: keep minimal, neutral, unaltered in layout
+ // Sofa centered against back wall
+ const sofaW = w * 0.38;
+ const sofaH = h * 0.16;
+ const sofaX = roomCenter - sofaW / 2;
+ const sofaY = floorHorizon - sofaH + 6;
+
+ ctx.save();
+ // Shadow under sofa
+ const sofaShadowGrad = ctx.createRadialGradient(
+ roomCenter, floorHorizon, sofaW * 0.1,
+ roomCenter, floorHorizon, sofaW * 0.7
+ );
+ sofaShadowGrad.addColorStop(0, 'rgba(0,0,0,0.28)');
+ sofaShadowGrad.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = sofaShadowGrad;
+ ctx.beginPath();
+ ctx.ellipse(roomCenter, floorHorizon + 2, sofaW * 0.5, sofaH * 0.27, 0, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Sofa body
+ const sofaGrad = ctx.createLinearGradient(sofaX, sofaY, sofaX, sofaY + sofaH);
+ sofaGrad.addColorStop(0, '#f2efe8');
+ sofaGrad.addColorStop(1, '#ded6c9');
+ ctx.fillStyle = sofaGrad;
+ const radius = 18;
+ ctx.beginPath();
+ ctx.moveTo(sofaX + radius, sofaY);
+ ctx.lineTo(sofaX + sofaW - radius, sofaY);
+ ctx.quadraticCurveTo(sofaX + sofaW, sofaY, sofaX + sofaW, sofaY + radius);
+ ctx.lineTo(sofaX + sofaW, sofaY + sofaH - radius);
+ ctx.quadraticCurveTo(sofaX + sofaW, sofaY + sofaH, sofaX + sofaW - radius, sofaY + sofaH);
+ ctx.lineTo(sofaX + radius, sofaY + sofaH);
+ ctx.quadraticCurveTo(sofaX, sofaY + sofaH, sofaX, sofaY + sofaH - radius);
+ ctx.lineTo(sofaX, sofaY + radius);
+ ctx.quadraticCurveTo(sofaX, sofaY, sofaX + radius, sofaY);
+ ctx.closePath();
+ ctx.fill();
+
+ // Seat division
+ ctx.strokeStyle = 'rgba(180,170,155,0.65)';
+ ctx.lineWidth = 1.2;
+ ctx.beginPath();
+ ctx.moveTo(sofaX + sofaW / 3, sofaY + sofaH * 0.18);
+ ctx.lineTo(sofaX + sofaW / 3, sofaY + sofaH - 10);
+ ctx.moveTo(sofaX + sofaW * 2 / 3, sofaY + sofaH * 0.18);
+ ctx.lineTo(sofaX + sofaW * 2 / 3, sofaY + sofaH - 10);
+ ctx.stroke();
+
+ // Back cushions suggestion
+ const cushionH = sofaH * 0.46;
+ const cushionY = sofaY + sofaH * 0.08;
+ const cushionGrad = ctx.createLinearGradient(sofaX, cushionY, sofaX, cushionY + cushionH);
+ cushionGrad.addColorStop(0, '#f7f4ee');
+ cushionGrad.addColorStop(1, '#e5ddd0');
+ ctx.fillStyle = cushionGrad;
+ ctx.beginPath();
+ ctx.moveTo(sofaX + radius, cushionY);
+ ctx.lineTo(sofaX + sofaW - radius, cushionY);
+ ctx.quadraticCurveTo(sofaX + sofaW, cushionY, sofaX + sofaW, cushionY + radius);
+ ctx.lineTo(sofaX + sofaW, cushionY + cushionH);
+ ctx.lineTo(sofaX, cushionY + cushionH);
+ ctx.lineTo(sofaX, cushionY + radius);
+ ctx.quadraticCurveTo(sofaX, cushionY, sofaX + radius, cushionY);
+ ctx.closePath();
+ ctx.fill();
+
+ // Arms
+ const armW = sofaW * 0.1;
+ const armGrad = ctx.createLinearGradient(0, sofaY, 0, sofaY + sofaH);
+ armGrad.addColorStop(0, '#e7e0d4');
+ armGrad.addColorStop(1, '#d5c9b6');
+ ctx.fillStyle = armGrad;
+ ctx.beginPath();
+ ctx.moveTo(sofaX - 2, sofaY + sofaH * 0.12);
+ ctx.lineTo(sofaX + armW, sofaY + sofaH * 0.12);
+ ctx.lineTo(sofaX + armW, sofaY + sofaH - 4);
+ ctx.quadraticCurveTo(sofaX + armW * 0.3, sofaY + sofaH + 4, sofaX - 2, sofaY + sofaH - 2);
+ ctx.closePath();
+ ctx.fill();
+
+ ctx.beginPath();
+ const rightArmX = sofaX + sofaW;
+ ctx.moveTo(rightArmX + 2, sofaY + sofaH * 0.12);
+ ctx.lineTo(rightArmX - armW, sofaY + sofaH * 0.12);
+ ctx.lineTo(rightArmX - armW, sofaY + sofaH - 4);
+ ctx.quadraticCurveTo(rightArmX - armW * 0.3, sofaY + sofaH + 4, rightArmX + 2, sofaY + sofaH - 2);
+ ctx.closePath();
+ ctx.fill();
+
+ // Sofa top highlight
+ const sofaTopGrad = ctx.createLinearGradient(sofaX, sofaY, sofaX, sofaY + sofaH * 0.4);
+ sofaTopGrad.addColorStop(0, 'rgba(255,255,255,0.6)');
+ sofaTopGrad.addColorStop(1, 'rgba(255,255,255,0)');
+ ctx.fillStyle = sofaTopGrad;
+ ctx.fillRect(sofaX + 4, sofaY + 2, sofaW - 8, sofaH * 0.4);
+
+ ctx.restore();
+
+ // Side console table on right wall
+ ctx.save();
+ const tableW = w * 0.16;
+ const tableH = h * 0.13;
+ const tableX = rightWallBottomRight.x - tableW * 0.75;
+ const tableY = floorHorizon - tableH + 4;
+
+ // Shadow
+ const tShadowGrad = ctx.createRadialGradient(
+ tableX + tableW * 0.55, floorHorizon + 2, tableW * 0.05,
+ tableX + tableW * 0.55, floorHorizon + 2, tableW * 0.5
+ );
+ tShadowGrad.addColorStop(0, 'rgba(0,0,0,0.3)');
+ tShadowGrad.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = tShadowGrad;
+ ctx.beginPath();
+ ctx.ellipse(tableX + tableW * 0.55, floorHorizon + 2, tableW * 0.5, tableH * 0.28, 0, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Top
+ const topGrad = ctx.createLinearGradient(tableX, tableY, tableX, tableY + tableH * 0.35);
+ topGrad.addColorStop(0, '#f3eee5');
+ topGrad.addColorStop(1, '#e0d4c3');
+ ctx.fillStyle = topGrad;
+ ctx.beginPath();
+ ctx.moveTo(tableX, tableY + 6);
+ ctx.lineTo(tableX + tableW, tableY + 6);
+ ctx.lineTo(tableX + tableW * 0.96, tableY + tableH * 0.3);
+ ctx.lineTo(tableX + tableW * 0.04, tableY + tableH * 0.3);
+ ctx.closePath();
+ ctx.fill();
+
+ // Legs
+ ctx.strokeStyle = '#b39a7b';
+ ctx.lineWidth = 5;
+ const legY1 = tableY + tableH * 0.32;
+ const legY2 = floorHorizon - 1;
+ ctx.beginPath();
+ ctx.moveTo(tableX + tableW * 0.12, legY1);
+ ctx.lineTo(tableX + tableW * 0.12, legY2);
+ ctx.moveTo(tableX + tableW * 0.88, legY1);
+ ctx.lineTo(tableX + tableW * 0.88, legY2);
+ ctx.stroke();
+
+ ctx.restore();
+
+ // Subtle global vignette for photorealism feel
+ ctx.save();
+ const vignetteGrad = ctx.createRadialGradient(
+ w / 2, h * 0.55, Math.min(w, h) * 0.2,
+ w / 2, h * 0.55, Math.max(w, h) * 0.9
+ );
+ vignetteGrad.addColorStop(0, 'rgba(0,0,0,0)');
+ vignetteGrad.addColorStop(1, 'rgba(0,0,0,0.26)');
+ ctx.fillStyle = vignetteGrad;
+ ctx.fillRect(0, 0, w, h);
+ ctx.restore();
+ }
+
+ // Initial canvas CSS sizing
+ function initSize() {
+ canvas.style.width = '100vw';
+ canvas.style.height = '72vh';
+ resize();
+ }
+ initSize();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/93946ee6d736/grok.html b/data/artifacts/93946ee6d736/grok.html
new file mode 100644
index 0000000..74ba392
--- /dev/null
+++ b/data/artifacts/93946ee6d736/grok.html
@@ -0,0 +1,706 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Luxury Wallpaper Visualization</title>
+<style>
+*{margin:0;padding:0;box-sizing:border-box}
+html,body{width:100%;height:100%;overflow:hidden;background:#0d0c0b;font-family:Georgia,"Times New Roman",serif}
+canvas{display:block;width:100%;height:100%}
+#hud{position:fixed;left:0;right:0;bottom:0;padding:18px 28px 22px;background:linear-gradient(180deg,transparent 0%,rgba(8,7,6,.92) 28%);color:#f3ebe0;pointer-events:none;z-index:10}
+#hud h1{font-size:15px;font-weight:400;letter-spacing:.18em;text-transform:uppercase;color:#c4a574;margin-bottom:6px}
+#hud h2{font-size:22px;font-weight:400;letter-spacing:.04em;margin-bottom:4px;color:#faf6ef}
+#hud .meta{font-size:12px;color:#a89880;letter-spacing:.06em;margin-bottom:10px}
+#hud a{color:#d4b896;text-decoration:none;border-bottom:1px solid rgba(212,184,150,.35);pointer-events:auto}
+#hud p{font-size:13px;line-height:1.55;color:#d8cfc0;max-width:820px;font-style:italic}
+#badge{position:fixed;top:16px;right:18px;z-index:10;background:rgba(20,16,12,.78);border:1px solid rgba(196,165,116,.35);color:#e8dcc8;font-size:10px;letter-spacing:.2em;text-transform:uppercase;padding:8px 12px;backdrop-filter:blur(6px)}
+</style>
+</head>
+<body>
+<div id="badge">New Arrivals · Designer Wallcoverings</div>
+<canvas id="c"></canvas>
+<div id="hud">
+<h1>Selected Wallpaper</h1>
+<h2 id="wname">Château Arabesque — Platinum & Champagne</h2>
+<div class="meta">Brand: Designer Wallcoverings · Collection: New Arrivals · <a id="wlink" href="https://www.designerwallcoverings.com/new-arrivals/" target="_blank" rel="noopener">designerwallcoverings.com/new-arrivals</a></div>
+<p id="wwhy">A refined large-scale arabesque damask in platinum metallic on warm champagne ground brings quiet grandeur to a luxury residence without overwhelming the architecture. Continuous application on every wall unifies the volume, while the classic motif and soft metallic sheen catch natural light with understated opulence appropriate to high-end interiors.</p>
+</div>
+<script>
+(function(){
+const canvas=document.getElementById('c');
+const ctx=canvas.getContext('2d');
+let W,H,dpr;
+
+/* Procedural luxury damask tile — single selected pattern used on ALL walls */
+function makeDamaskTile(size){
+ const tc=document.createElement('canvas');
+ tc.width=size;tc.height=size;
+ const g=tc.getContext('2d');
+ /* champagne ground */
+ const bg=g.createLinearGradient(0,0,size,size);
+ bg.addColorStop(0,'#e8d9c0');
+ bg.addColorStop(0.45,'#dfd0b4');
+ bg.addColorStop(1,'#d4c4a6');
+ g.fillStyle=bg;
+ g.fillRect(0,0,size,size);
+ /* subtle paper fiber */
+ for(let i=0;i<900;i++){
+ const x=Math.random()*size,y=Math.random()*size;
+ g.fillStyle=`rgba(120,100,70,${0.015+Math.random()*0.03})`;
+ g.fillRect(x,y,1.2,1.2);
+ }
+ /* metallic platinum ink */
+ function petal(cx,cy,r,rot,alpha){
+ g.save();
+ g.translate(cx,cy);g.rotate(rot);
+ g.beginPath();
+ g.moveTo(0,-r*0.15);
+ g.bezierCurveTo(r*0.55,-r*0.9,r*0.95,-r*0.35,0,r*0.95);
+ g.bezierCurveTo(-r*0.95,-r*0.35,-r*0.55,-r*0.9,0,-r*0.15);
+ g.closePath();
+ const grd=g.createLinearGradient(-r, -r, r, r);
+ grd.addColorStop(0,`rgba(175,170,160,${alpha})`);
+ grd.addColorStop(0.5,`rgba(230,225,215,${alpha*1.15})`);
+ grd.addColorStop(1,`rgba(140,135,125,${alpha})`);
+ g.fillStyle=grd;
+ g.fill();
+ g.strokeStyle=`rgba(110,105,95,${alpha*0.55})`;
+ g.lineWidth=0.8;
+ g.stroke();
+ g.restore();
+ }
+ function medallion(cx,cy,s,a){
+ /* central diamond / fleur structure */
+ for(let k=0;k<8;k++){
+ petal(cx,cy,s*0.42,k*Math.PI/4 + Math.PI/8,a*0.85);
+ }
+ for(let k=0;k<4;k++){
+ petal(cx,cy,s*0.28,k*Math.PI/2,a);
+ }
+ /* inner jewel */
+ g.beginPath();
+ g.arc(cx,cy,s*0.07,0,Math.PI*2);
+ g.fillStyle=`rgba(245,240,230,${a*1.1})`;
+ g.fill();
+ g.strokeStyle=`rgba(130,120,100,${a*0.7})`;
+ g.lineWidth=1;
+ g.stroke();
+ /* outer ring of dots */
+ for(let k=0;k<12;k++){
+ const ang=k*Math.PI/6;
+ const dx=cx+Math.cos(ang)*s*0.52;
+ const dy=cy+Math.sin(ang)*s*0.52;
+ g.beginPath();
+ g.arc(dx,dy,s*0.025,0,Math.PI*2);
+ g.fillStyle=`rgba(190,185,175,${a*0.75})`;
+ g.fill();
+ }
+ /* scrolling vines */
+ g.save();
+ g.strokeStyle=`rgba(160,150,135,${a*0.65})`;
+ g.lineWidth=1.4;
+ for(let q=0;q<4;q++){
+ g.beginPath();
+ const base=q*Math.PI/2;
+ g.moveTo(cx+Math.cos(base)*s*0.55,cy+Math.sin(base)*s*0.55);
+ g.bezierCurveTo(
+ cx+Math.cos(base+0.4)*s*0.85, cy+Math.sin(base+0.4)*s*0.85,
+ cx+Math.cos(base+0.9)*s*0.9, cy+Math.sin(base+0.9)*s*0.9,
+ cx+Math.cos(base+1.2)*s*0.62, cy+Math.sin(base+1.2)*s*0.62
+ );
+ g.stroke();
+ }
+ g.restore();
+ }
+ /* primary lattice of medallions — classic damask repeat */
+ const cell=size/2;
+ medallion(cell*0.5,cell*0.5,cell*0.92,0.55);
+ medallion(cell*1.5,cell*0.5,cell*0.92,0.55);
+ medallion(cell*0.5,cell*1.5,cell*0.92,0.55);
+ medallion(cell*1.5,cell*1.5,cell*0.92,0.55);
+ /* secondary half-drop accents for true damask feel */
+ medallion(cell,cell,cell*0.55,0.42);
+ medallion(0,cell,cell*0.55,0.42);
+ medallion(cell,0,cell*0.55,0.42);
+ medallion(size,cell,cell*0.55,0.42);
+ medallion(cell,size,cell*0.55,0.42);
+ /* fine corner filigree */
+ g.strokeStyle='rgba(150,140,120,0.28)';
+ g.lineWidth=1;
+ for(let i=0;i<4;i++){
+ for(let j=0;j<4;j++){
+ const x=i*cell/2 + cell/4, y=j*cell/2 + cell/4;
+ g.beginPath();
+ g.arc(x,y,6,0,Math.PI*2);
+ g.stroke();
+ }
+ }
+ /* soft sheen wash */
+ const sheen=g.createLinearGradient(0,0,size,size*0.3);
+ sheen.addColorStop(0,'rgba(255,252,245,0.08)');
+ sheen.addColorStop(0.5,'rgba(255,255,255,0)');
+ sheen.addColorStop(1,'rgba(255,250,240,0.06)');
+ g.fillStyle=sheen;
+ g.fillRect(0,0,size,size);
+ return tc;
+}
+
+let tileCanvas=null;
+let tilePattern=null;
+const TILE=256;
+
+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);
+ if(!tileCanvas){tileCanvas=makeDamaskTile(TILE);}
+ tilePattern=ctx.createPattern(tileCanvas,'repeat');
+ draw();
+}
+
+/* Perspective helpers */
+function project(x,y,z,cam){
+ const dx=x-cam.x, dy=y-cam.y, dz=z-cam.z;
+ const fov=cam.fov;
+ const s=fov/Math.max(0.1,dz);
+ return {x:W/2+dx*s, y:H*0.42+dy*s, s:s, z:dz};
+}
+
+function quad(p0,p1,p2,p3, fillFn){
+ ctx.beginPath();
+ ctx.moveTo(p0.x,p0.y);
+ ctx.lineTo(p1.x,p1.y);
+ ctx.lineTo(p2.x,p2.y);
+ ctx.lineTo(p3.x,p3.y);
+ ctx.closePath();
+ fillFn();
+}
+
+function drawWallpaperQuad(worldPts, cam, lightMul, shadeSide){
+ /* worldPts: 4 corners TL TR BR BL in 3D */
+ const p=worldPts.map(pt=>project(pt[0],pt[1],pt[2],cam));
+ if(p[0].z<=0.2||p[1].z<=0.2||p[2].z<=0.2||p[3].z<=0.2) return;
+
+ /* Clip path */
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(p[0].x,p[0].y);
+ ctx.lineTo(p[1].x,p[1].y);
+ ctx.lineTo(p[2].x,p[2].y);
+ ctx.lineTo(p[3].x,p[3].y);
+ ctx.closePath();
+ ctx.clip();
+
+ /* Affine-ish pattern mapping via transform approximation using edges */
+ /* Compute bounding box and draw tiled pattern with perspective skew via multiple strips */
+ const steps=28;
+ for(let i=0;i<steps;i++){
+ const t0=i/steps, t1=(i+1)/steps;
+ /* interpolate top and bottom edges */
+ const topA={x:worldPts[0][0]+(worldPts[1][0]-worldPts[0][0])*t0,
+ y:worldPts[0][1]+(worldPts[1][1]-worldPts[0][1])*t0,
+ z:worldPts[0][2]+(worldPts[1][2]-worldPts[0][2])*t0};
+ const topB={x:worldPts[0][0]+(worldPts[1][0]-worldPts[0][0])*t1,
+ y:worldPts[0][1]+(worldPts[1][1]-worldPts[0][1])*t1,
+ z:worldPts[0][2]+(worldPts[1][2]-worldPts[0][2])*t1};
+ const botA={x:worldPts[3][0]+(worldPts[2][0]-worldPts[3][0])*t0,
+ y:worldPts[3][1]+(worldPts[2][1]-worldPts[3][1])*t0,
+ z:worldPts[3][2]+(worldPts[2][2]-worldPts[3][2])*t0};
+ const botB={x:worldPts[3][0]+(worldPts[2][0]-worldPts[3][0])*t1,
+ y:worldPts[3][1]+(worldPts[2][1]-worldPts[3][1])*t1,
+ z:worldPts[3][2]+(worldPts[2][2]-worldPts[3][2])*t1};
+ const pa=project(topA.x,topA.y,topA.z,cam);
+ const pb=project(topB.x,topB.y,topB.z,cam);
+ const pc=project(botB.x,botB.y,botB.z,cam);
+ const pd=project(botA.x,botA.y,botA.z,cam);
+
+ /* UV along wall width — keep continuous scale ~ 0.9m repeat */
+ const u0=t0, u1=t1;
+ /* draw strip with pattern using transformed image */
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(pa.x,pa.y);ctx.lineTo(pb.x,pb.y);ctx.lineTo(pc.x,pc.y);ctx.lineTo(pd.x,pd.y);
+ ctx.closePath();
+ ctx.clip();
+
+ /* Map tile across vertical span with approximate affine */
+ const hSteps=16;
+ for(let j=0;j<hSteps;j++){
+ const v0=j/hSteps, v1=(j+1)/hSteps;
+ const q0={x:pa.x+(pd.x-pa.x)*v0,y:pa.y+(pd.y-pa.y)*v0};
+ const q1={x:pb.x+(pc.x-pb.x)*v0,y:pb.y+(pc.y-pb.y)*v0};
+ const q2={x:pb.x+(pc.x-pb.x)*v1,y:pb.y+(pc.y-pb.y)*v1};
+ const q3={x:pa.x+(pd.x-pa.x)*v1,y:pa.y+(pd.y-pa.y)*v1};
+ /* source rect in tile space — continuous world UV */
+ /* wall width parameter drives U; V from top to bottom */
+ const wallW=Math.hypot(worldPts[1][0]-worldPts[0][0], worldPts[1][2]-worldPts[0][2]);
+ const wallH=Math.abs(worldPts[3][1]-worldPts[0][1]);
+ /* scale: ~ TILE px = 0.75 world units for realistic damask scale */
+ const scale=TILE/0.75;
+ const sx=u0*wallW*scale;
+ const sy=v0*wallH*scale;
+ const sw=Math.max(1,(u1-u0)*wallW*scale);
+ const sh=Math.max(1,(v1-v0)*wallH*scale);
+
+ /* destination quad — use drawImage with rough bounds then shade */
+ const minx=Math.min(q0.x,q1.x,q2.x,q3.x);
+ const maxx=Math.max(q0.x,q1.x,q2.x,q3.x);
+ const miny=Math.min(q0.y,q1.y,q2.y,q3.y);
+ const maxy=Math.max(q0.y,q1.y,q2.y,q3.y);
+ const dw=maxx-minx, dh=maxy-miny;
+ if(dw<0.5||dh<0.5) continue;
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(q0.x,q0.y);ctx.lineTo(q1.x,q1.y);ctx.lineTo(q2.x,q2.y);ctx.lineTo(q3.x,q3.y);
+ ctx.closePath();
+ ctx.clip();
+
+ /* bilinear-ish: draw pattern image stretched to strip cell */
+ /* sample from repeating tile */
+ const ox= ((sx%TILE)+TILE)%TILE;
+ const oy= ((sy%TILE)+TILE)%TILE;
+ /* draw enough tiles to cover */
+ for(let tx=-TILE;tx<dw+TILE;tx+=TILE){
+ for(let ty=-TILE;ty<dh+TILE;ty+=TILE){
+ ctx.drawImage(tileCanvas, minx+tx - ox*(dw/sw), miny+ty - oy*(dh/sh), TILE*(dw/sw), TILE*(dh/sh));
+ }
+ }
+ ctx.restore();
+ }
+ ctx.restore();
+ }
+
+ /* lighting / ambient occlusion overlays for photorealism */
+ const g=ctx.createLinearGradient(p[0].x,p[0].y,p[3].x,p[3].y);
+ if(shadeSide==='back'){
+ g.addColorStop(0,`rgba(40,30,20,${0.18*lightMul})`);
+ g.addColorStop(0.5,`rgba(30,22,15,${0.08*lightMul})`);
+ g.addColorStop(1,`rgba(20,15,10,${0.28*lightMul})`);
+ } else if(shadeSide==='left'){
+ g.addColorStop(0,`rgba(25,18,12,${0.34*lightMul})`);
+ g.addColorStop(1,`rgba(25,18,12,${0.22*lightMul})`);
+ } else if(shadeSide==='right'){
+ g.addColorStop(0,`rgba(55,45,30,${0.12*lightMul})`);
+ g.addColorStop(1,`rgba(40,30,20,${0.2*lightMul})`);
+ } else {
+ g.addColorStop(0,`rgba(0,0,0,${0.1*lightMul})`);
+ g.addColorStop(1,`rgba(0,0,0,${0.15*lightMul})`);
+ }
+ ctx.fillStyle=g;
+ ctx.beginPath();
+ ctx.moveTo(p[0].x,p[0].y);ctx.lineTo(p[1].x,p[1].y);ctx.lineTo(p[2].x,p[2].y);ctx.lineTo(p[3].x,p[3].y);
+ ctx.closePath();
+ ctx.fill();
+
+ /* top soft light from window */
+ const lg=ctx.createLinearGradient(p[0].x,p[0].y,p[1].x,p[1].y);
+ lg.addColorStop(0,'rgba(255,248,230,0.03)');
+ lg.addColorStop(0.55,'rgba(255,250,235,0.10)');
+ lg.addColorStop(1,'rgba(255,248,230,0.04)');
+ ctx.fillStyle=lg;
+ ctx.fill();
+
+ /* corner AO */
+ const cx=(p[0].x+p[1].x+p[2].x+p[3].x)/4;
+ const cy=(p[0].y+p[1].y+p[2].y+p[3].y)/4;
+ /* vertical edge darkening */
+ ctx.strokeStyle='rgba(30,22,14,0.18)';
+ ctx.lineWidth=2;
+ ctx.beginPath();ctx.moveTo(p[0].x,p[0].y);ctx.lineTo(p[3].x,p[3].y);ctx.stroke();
+ ctx.beginPath();ctx.moveTo(p[1].x,p[1].y);ctx.lineTo(p[2].x,p[2].y);ctx.stroke();
+
+ ctx.restore();
+}
+
+function drawFloor(cam){
+ const y=1.2;
+ const pts=[
+ [-3.2,y,2.2],[3.2,y,2.2],[3.2,y,9.5],[-3.2,y,9.5]
+ ].map(p=>project(p[0],p[1],p[2],cam));
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(pts[0].x,pts[0].y);
+ ctx.lineTo(pts[1].x,pts[1].y);
+ ctx.lineTo(pts[2].x,pts[2].y);
+ ctx.lineTo(pts[3].x,pts[3].y);
+ ctx.closePath();
+ const fg=ctx.createLinearGradient(0,pts[0].y,0,pts[2].y);
+ fg.addColorStop(0,'#5a4030');
+ fg.addColorStop(0.4,'#4a3428');
+ fg.addColorStop(1,'#2c1e16');
+ ctx.fillStyle=fg;
+ ctx.fill();
+ /* herringbone planks */
+ ctx.save();
+ ctx.clip();
+ const rows=40;
+ for(let r=0;r<rows;r++){
+ const t0=r/rows,t1=(r+1)/rows;
+ const z0=2.2+(9.5-2.2)*t0;
+ const z1=2.2+(9.5-2.2)*t1;
+ for(let c=-6;c<=6;c++){
+ const x0=c*0.55 + (r%2?0.27:0);
+ const x1=x0+0.52;
+ const a=project(x0,y,z0,cam);
+ const b=project(x1,y,z0,cam);
+ const cpt=project(x1,y,z1,cam);
+ const d=project(x0,y,z1,cam);
+ ctx.beginPath();
+ ctx.moveTo(a.x,a.y);ctx.lineTo(b.x,b.y);ctx.lineTo(cpt.x,cpt.y);ctx.lineTo(d.x,d.y);
+ ctx.closePath();
+ const tone= ((c+r+20)%3===0)? '#6b4a36':'#5c3f2e';
+ ctx.fillStyle=tone;
+ ctx.fill();
+ ctx.strokeStyle='rgba(25,15,10,0.35)';
+ ctx.lineWidth=0.6;
+ ctx.stroke();
+ }
+ }
+ /* floor gloss */
+ const gl=ctx.createLinearGradient(W*0.5,pts[0].y,W*0.7,pts[2].y);
+ gl.addColorStop(0,'rgba(255,240,210,0.05)');
+ gl.addColorStop(0.4,'rgba(255,245,220,0.12)');
+ gl.addColorStop(1,'rgba(255,240,210,0.02)');
+ ctx.fillStyle=gl;
+ ctx.fillRect(0,0,W,H);
+ ctx.restore();
+ ctx.restore();
+}
+
+function drawCeiling(cam){
+ const y=-1.55;
+ const pts=[
+ [-3.2,y,2.2],[3.2,y,2.2],[3.2,y,9.5],[-3.2,y,9.5]
+ ].map(p=>project(p[0],p[1],p[2],cam));
+ ctx.beginPath();
+ ctx.moveTo(pts[0].x,pts[0].y);
+ ctx.lineTo(pts[1].x,pts[1].y);
+ ctx.lineTo(pts[2].x,pts[2].y);
+ ctx.lineTo(pts[3].x,pts[3].y);
+ ctx.closePath();
+ const cg=ctx.createLinearGradient(0,pts[2].y,0,pts[0].y);
+ cg.addColorStop(0,'#f2ebe0');
+ cg.addColorStop(1,'#e0d6c8');
+ ctx.fillStyle=cg;
+ ctx.fill();
+ /* crown shadow */
+ ctx.strokeStyle='rgba(80,60,40,0.12)';
+ ctx.lineWidth=6;
+ ctx.stroke();
+}
+
+function drawTrim(a,b,cam,thick){
+ const p0=project(a[0],a[1],a[2],cam);
+ const p1=project(b[0],b[1],b[2],cam);
+ ctx.strokeStyle='#f5f0e6';
+ ctx.lineWidth=thick||4;
+ ctx.lineCap='square';
+ ctx.beginPath();
+ ctx.moveTo(p0.x,p0.y);
+ ctx.lineTo(p1.x,p1.y);
+ ctx.stroke();
+ ctx.strokeStyle='rgba(0,0,0,0.12)';
+ ctx.lineWidth=1;
+ ctx.stroke();
+}
+
+function roundRectPath(x,y,w,h,r){
+ ctx.beginPath();
+ ctx.moveTo(x+r,y);
+ ctx.arcTo(x+w,y,x+w,y+h,r);
+ ctx.arcTo(x+w,y+h,x,y+h,r);
+ ctx.arcTo(x,y+h,x,y,r);
+ ctx.arcTo(x,y,x+w,y,r);
+ ctx.closePath();
+}
+
+function drawFurniture(cam){
+ /* sofa — low modern luxury, charcoal velvet */
+ function box(x0,y0,z0,x1,y1,z1,color,shade){
+ const faces=[
+ /* front */
+ [[x0,y0,z0],[x1,y0,z0],[x1,y1,z0],[x0,y1,z0], 1],
+ /* top */
+ [[x0,y0,z1],[x1,y0,z1],[x1,y0,z0],[x0,y0,z0], 1.15],
+ /* side L */
+ [[x0,y0,z1],[x0,y0,z0],[x0,y1,z0],[x0,y1,z1], 0.75],
+ /* side R */
+ [[x1,y0,z0],[x1,y0,z1],[x1,y1,z1],[x1,y1,z0], 0.9],
+ ];
+ faces.forEach(f=>{
+ const pts=f.slice(0,4).map(p=>project(p[0],p[1],p[2],cam));
+ if(pts.some(p=>p.z<0.2))return;
+ ctx.beginPath();
+ ctx.moveTo(pts[0].x,pts[0].y);
+ pts.slice(1).forEach(p=>ctx.lineTo(p.x,p.y));
+ ctx.closePath();
+ const m=f[4]*shade;
+ const c=color;
+ ctx.fillStyle=`rgba(${c[0]*m|0},${c[1]*m|0},${c[2]*m|0},1)`;
+ ctx.fill();
+ ctx.strokeStyle='rgba(0,0,0,0.15)';
+ ctx.lineWidth=1;
+ ctx.stroke();
+ });
+ }
+ /* sofa body */
+ box(-1.5,0.55,5.2, 1.5,1.2,6.4, [48,42,38],1);
+ /* back cushions */
+ box(-1.45,0.05,6.15, 1.45,0.55,6.38, [42,36,32],1);
+ /* seat cushions */
+ box(-1.4,0.45,5.3, -0.05,0.58,6.15, [55,48,44],1.05);
+ box(0.05,0.45,5.3, 1.4,0.58,6.15, [55,48,44],1.05);
+ /* arms */
+ box(-1.55,0.25,5.25, -1.35,1.15,6.4, [40,35,32],0.95);
+ box(1.35,0.25,5.25, 1.55,1.15,6.4, [40,35,32],0.95);
+
+ /* coffee table — travertine + bronze */
+ box(-0.7,0.85,4.2, 0.7,1.05,5.0, [210,200,185],1);
+ box(-0.65,1.05,4.3, -0.5,1.2,4.45, [90,70,50],0.9);
+ box(0.5,1.05,4.3, 0.65,1.2,4.45, [90,70,50],0.9);
+ box(-0.65,1.05,4.8, -0.5,1.2,4.95, [90,70,50],0.9);
+ box(0.5,1.05,4.8, 0.65,1.2,4.95, [90,70,50],0.9);
+
+ /* sideboard against back wall */
+ box(-2.2,0.35,8.55, 2.2,1.2,8.95, [55,40,30],1);
+ box(-2.15,0.3,8.5, 2.15,0.38,8.9, [70,52,38],1.1);
+ /* drawer lines */
+ const d1=project(-1.8,0.55,8.5,cam);
+ const d2=project(1.8,0.55,8.5,cam);
+ ctx.strokeStyle='rgba(30,20,12,0.35)';
+ ctx.lineWidth=1;
+ ctx.beginPath();ctx.moveTo(d1.x,d1.y);ctx.lineTo(d2.x,d2.y);ctx.stroke();
+ const d3=project(-1.8,0.85,8.5,cam);
+ const d4=project(1.8,0.85,8.5,cam);
+ ctx.beginPath();ctx.moveTo(d3.x,d3.y);ctx.lineTo(d4.x,d4.y);ctx.stroke();
+
+ /* two lounge chairs flanking */
+ box(-2.6,0.55,4.6, -1.7,1.2,5.5, [70,55,45],1);
+ box(-2.55,0.2,5.3, -1.75,0.55,5.5, [65,50,42],1);
+ box(1.7,0.55,4.6, 2.6,1.2,5.5, [70,55,45],1);
+ box(1.75,0.2,5.3, 2.55,0.55,5.5, [65,50,42],1);
+
+ /* rug under seating */
+ const rug=[
+ project(-2.0,1.195,4.0,cam),
+ project(2.0,1.195,4.0,cam),
+ project(2.2,1.195,6.7,cam),
+ project(-2.2,1.195,6.7,cam)
+ ];
+ ctx.beginPath();
+ ctx.moveTo(rug[0].x,rug[0].y);
+ rug.slice(1).forEach(p=>ctx.lineTo(p.x,p.y));
+ ctx.closePath();
+ ctx.fillStyle='rgba(90,50,40,0.55)';
+ ctx.fill();
+ ctx.strokeStyle='rgba(60,30,25,0.4)';
+ ctx.lineWidth=2;
+ ctx.stroke();
+ /* rug border pattern */
+ ctx.save();
+ ctx.clip();
+ ctx.strokeStyle='rgba(180,140,100,0.25)';
+ ctx.lineWidth=8;
+ ctx.stroke();
+ ctx.restore();
+
+ /* floor lamp */
+ const lx=-2.35, lz=3.9;
+ const base=project(lx,1.2,lz,cam);
+ const top=project(lx,-0.35,lz,cam);
+ ctx.strokeStyle='#8a7348';
+ ctx.lineWidth=3;
+ ctx.beginPath();ctx.moveTo(base.x,base.y);ctx.lineTo(top.x,top.y);ctx.stroke();
+ const shadeR=top.s*0.22;
+ const sg=ctx.createRadialGradient(top.x,top.y+shadeR*0.2,1,top.x,top.y,shadeR);
+ sg.addColorStop(0,'rgba(255,245,220,0.95)');
+ sg.addColorStop(0.6,'rgba(235,210,170,0.85)');
+ sg.addColorStop(1,'rgba(180,150,110,0.5)');
+ ctx.beginPath();
+ ctx.ellipse(top.x,top.y+10,shadeR,shadeR*0.7,0,0,Math.PI*2);
+ ctx.fillStyle=sg;
+ ctx.fill();
+ /* lamp glow */
+ const glow=ctx.createRadialGradient(top.x,top.y,10,top.x,top.y,shadeR*3);
+ glow.addColorStop(0,'rgba(255,230,180,0.12)');
+ glow.addColorStop(1,'rgba(255,230,180,0)');
+ ctx.fillStyle=glow;
+ ctx.beginPath();ctx.arc(top.x,top.y,shadeR*3,0,Math.PI*2);ctx.fill();
+
+ /* simple art-free console objects: vase */
+ const vx=0, vz=8.45;
+ const vb=project(vx,0.3,vz,cam);
+ const vt=project(vx,-0.15,vz,cam);
+ ctx.fillStyle='#cfc6b4';
+ ctx.beginPath();
+ ctx.moveTo(vb.x-vb.s*0.08,vb.y);
+ ctx.quadraticCurveTo(vb.x-vb.s*0.14,(vb.y+vt.y)/2, vt.x-vt.s*0.05,vt.y);
+ ctx.lineTo(vt.x+vt.s*0.05,vt.y);
+ ctx.quadraticCurveTo(vb.x+vb.s*0.14,(vb.y+vt.y)/2, vb.x+vb.s*0.08,vb.y);
+ ctx.closePath();
+ ctx.fill();
+ ctx.strokeStyle='rgba(80,60,40,0.3)';
+ ctx.stroke();
+}
+
+function drawWindow(cam){
+ /* right wall window opening — preserve architecture, light source */
+ const frame=[
+ [3.18,-1.05,3.4],[3.18,-1.05,5.6],[3.18,0.85,5.6],[3.18,0.85,3.4]
+ ];
+ const pf=frame.map(p=>project(p[0],p[1],p[2],cam));
+ /* exterior light */
+ ctx.beginPath();
+ ctx.moveTo(pf[0].x,pf[0].y);
+ pf.slice(1).forEach(p=>ctx.lineTo(p.x,p.y));
+ ctx.closePath();
+ const sky=ctx.createLinearGradient(pf[0].x,pf[0].y,pf[2].x,pf[2].y);
+ sky.addColorStop(0,'#dce8f2');
+ sky.addColorStop(0.45,'#f2f0e8');
+ sky.addColorStop(1,'#c5d4a8');
+ ctx.fillStyle=sky;
+ ctx.fill();
+ /* soft exterior suggestion */
+ ctx.fillStyle='rgba(255,255,255,0.35)';
+ ctx.fill();
+ /* muntins */
+ ctx.strokeStyle='rgba(245,240,230,0.95)';
+ ctx.lineWidth=5;
+ ctx.stroke();
+ const midT=project(3.18,-1.05,4.5,cam);
+ const midB=project(3.18,0.85,4.5,cam);
+ ctx.lineWidth=3;
+ ctx.beginPath();ctx.moveTo(midT.x,midT.y);ctx.lineTo(midB.x,midB.y);ctx.stroke();
+ const midL=project(3.18,-0.1,3.4,cam);
+ const midR=project(3.18,-0.1,5.6,cam);
+ ctx.beginPath();ctx.moveTo(midL.x,midL.y);ctx.lineTo(midR.x,midR.y);ctx.stroke();
+ /* frame depth */
+ ctx.strokeStyle='#f7f2e8';
+ ctx.lineWidth=10;
+ ctx.stroke();
+ ctx.strokeStyle='rgba(0,0,0,0.15)';
+ ctx.lineWidth=1;
+ ctx.stroke();
+ /* light shaft on floor / air */
+ const beam=ctx.createLinearGradient(pf[1].x,pf[1].y, W*0.45, H*0.7);
+ beam.addColorStop(0,'rgba(255,248,230,0.22)');
+ beam.addColorStop(0.5,'rgba(255,245,220,0.06)');
+ beam.addColorStop(1,'rgba(255,245,220,0)');
+ ctx.fillStyle=beam;
+ ctx.beginPath();
+ ctx.moveTo(pf[0].x,pf[0].y);
+ ctx.lineTo(pf[1].x,pf[1].y);
+ ctx.lineTo(W*0.35,H*0.78);
+ ctx.lineTo(W*0.55,H*0.78);
+ ctx.closePath();
+ ctx.fill();
+}
+
+function draw(){
+ ctx.clearRect(0,0,W,H);
+ /* ambient backdrop */
+ const bg=ctx.createLinearGradient(0,0,0,H);
+ bg.addColorStop(0,'#1a1612');
+ bg.addColorStop(1,'#0c0a08');
+ ctx.fillStyle=bg;
+ ctx.fillRect(0,0,W,H);
+
+ const cam={x:0,y:0.15,z:0.15,fov:Math.min(W,H)*0.72};
+
+ /* Room dimensions:
+ x: -3.2 .. 3.2
+ y: -1.55 (ceil) .. 1.2 (floor)
+ z: 2.2 (near) .. 9.2 (far)
+ ALL four walls get the SAME damask pattern continuously
+ */
+
+ drawCeiling(cam);
+ drawFloor(cam);
+
+ /* BACK WALL — full coverage */
+ drawWallpaperQuad([
+ [-3.2,-1.55,9.15],
+ [ 3.2,-1.55,9.15],
+ [ 3.2, 1.20,9.15],
+ [-3.2, 1.20,9.15]
+ ], cam, 1.0, 'back');
+
+ /* LEFT WALL — full coverage, continuous pattern */
+ drawWallpaperQuad([
+ [-3.2,-1.55,2.3],
+ [-3.2,-1.55,9.15],
+ [-3.2, 1.20,9.15],
+ [-3.2, 1.20,2.3]
+ ], cam, 1.0, 'left');
+
+ /* RIGHT WALL — full coverage (window cut handled after as overlay of architecture) */
+ drawWallpaperQuad([
+ [3.2,-1.55,9.15],
+ [3.2,-1.55,2.3],
+ [3.2, 1.20,2.3],
+ [3.2, 1.20,9.15]
+ ], cam, 1.0, 'right');
+
+ /* NEAR side wall returns (thin reveals for depth) — still same wallpaper */
+ drawWallpaperQuad([
+ [-3.2,-1.55,2.3],
+ [-2.9,-1.55,2.3],
+ [-2.9, 1.20,2.3],
+ [-3.2, 1.20,2.3]
+ ], cam, 1.05, 'back');
+ drawWallpaperQuad([
+ [2.9,-1.55,2.3],
+ [3.2,-1.55,2.3],
+ [3.2, 1.20,2.3],
+ [2.9, 1.20,2.3]
+ ], cam, 1.05, 'back');
+
+ /* Baseboards & crown — preserve trim */
+ const yFloor=1.2, yCeil=-1.55;
+ /* back base */
+ drawTrim([-3.2,yFloor-0.02,9.12],[3.2,yFloor-0.02,9.12],cam,5);
+ drawTrim([-3.2,yCeil+0.05,9.12],[3.2,yCeil+0.05,9.12],cam,6);
+ /* left */
+ drawTrim([-3.18,yFloor-0.02,2.35],[-3.18,yFloor-0.02,9.12],cam,5);
+ drawTrim([-3.18,yCeil+0.05,2.35],[-3.18,yCeil+0.05,9.12],cam,6);
+ /* right */
+ drawTrim([3.18,yFloor-0.02,2.35],[3.18,yFloor-0.02,9.12],cam,5);
+ drawTrim([3.18,yCeil+0.05,2.35],[3.18,yCeil+0.05,9.12],cam,6);
+
+ /* chair rail mid molding continuous */
+ drawTrim([-3.18,0.15,2.35],[-3.18,0.15,9.12],cam,2);
+ drawTrim([-3.18,0.15,9.12],[3.18,0.15,9.12],cam,2);
+ drawTrim([3.18,0.15,9.12],[3.18,0.15,2.35],cam,2);
+
+ drawWindow(cam);
+ drawFurniture(cam);
+
+ /* subtle vignette */
+ const vig=ctx.createRadialGradient(W/2,H*0.45,H*0.2,W/2,H*0.45,H*0.85);
+ vig.addColorStop(0,'rgba(0,0,0,0)');
+ vig.addColorStop(1,'rgba(0,0,0,0.35)');
+ ctx.fillStyle=vig;
+ ctx.fillRect(0,0,W,H);
+
+ /* film grain */
+ const id=ctx.getImageData(0,0,Math.min(canvas.width,1200),Math.min(canvas.height,800));
+ /* skip heavy grain for perf — light CSS-like noise via sparse pixels */
+ ctx.globalAlpha=0.035;
+ for(let i=0;i<1200;i++){
+ const x=Math.random()*W, y=Math.random()*H;
+ const v=200+Math.random()*55;
+ ctx.fillStyle=`rgb(${v},${v},${v})`;
+ ctx.fillRect(x,y,1.2,1.2);
+ }
+ ctx.globalAlpha=1;
+}
+
+window.addEventListener('resize',resize);
+resize();
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/93946ee6d736/hermes3-8b.html b/data/artifacts/93946ee6d736/hermes3-8b.html
new file mode 100644
index 0000000..8ebe25b
--- /dev/null
+++ b/data/artifacts/93946ee6d736/hermes3-8b.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Architectural Visualization</title>
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ background-color: #f0f0f0;
+ }
+ .room {
+ width: 100%;
+ max-width: 600px;
+ margin: auto;
+ perspective: 1000px;
+ transform-style: preserve-3d;
+ }
+ .wall {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-image: url('https://i.imgur.com/4X5tZjT.jpg');
+ background-size: 12.5in 25in;
+ width: 100%;
+ height: 50vw;
+ border: 0;
+ outline: 0;
+ }
+ .back {
+ transform: rotateY(180deg);
+ }
+ .left, .right {
+ width: 33.333%;
+ backface-visibility: hidden;
+ position: relative;
+ z-index: 1;
+ }
+ .bottom, .top {
+ height: 50vw;
+ }
+ .back {
+ transform-origin: center left;
+ }
+ </style>
+</head>
+<body>
+ <div class="room">
+ <div class="wall front"></div>
+ <div class="wall back">
+ </div>
+ <div class="wall left">
+ </div>
+ <div class="wall right">
+ </div>
+ </div>
+
+ <script>
+ document.querySelectorAll('.wall').forEach(wall => {
+ wall.style.backgroundPosition = '-1px -1px';
+ setTimeout(() => {
+ wall.style.backgroundPosition = '0 0';
+ }, 50);
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/93946ee6d736/kimi.html b/data/artifacts/93946ee6d736/kimi.html
new file mode 100644
index 0000000..155d7dc
--- /dev/null
+++ b/data/artifacts/93946ee6d736/kimi.html
@@ -0,0 +1,466 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Luxury Interior Visualization - Lattice Noir Metallic</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ background: #0a0a0a;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ }
+ #canvas {
+ display: block;
+ width: 100vw;
+ height: 100vh;
+ }
+ #info {
+ position: absolute;
+ bottom: 30px;
+ left: 30px;
+ background: rgba(255,255,255,0.96);
+ padding: 25px;
+ border-radius: 12px;
+ max-width: 450px;
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
+ border: 1px solid rgba(0,0,0,0.1);
+ }
+ h2 {
+ margin: 0 0 12px 0;
+ font-size: 20px;
+ color: #1a1a1a;
+ font-weight: 600;
+ letter-spacing: -0.5px;
+ }
+ .meta {
+ margin: 8px 0;
+ font-size: 14px;
+ color: #444;
+ line-height: 1.5;
+ }
+ .label {
+ font-weight: 600;
+ color: #222;
+ }
+ a {
+ color: #0066cc;
+ text-decoration: none;
+ border-bottom: 1px solid transparent;
+ transition: border-color 0.2s;
+ }
+ a:hover {
+ border-bottom-color: #0066cc;
+ }
+ .description {
+ margin-top: 15px;
+ padding-top: 15px;
+ border-top: 1px solid #e0e0e0;
+ font-size: 14px;
+ color: #333;
+ line-height: 1.6;
+ font-style: italic;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="canvas"></canvas>
+ <div id="info">
+ <h2>Lattice Noir Metallic</h2>
+ <div class="meta"><span class="label">Brand:</span> Designer Wallcoverings</div>
+ <div class="meta"><span class="label">Collection:</span> New Arrivals 2024</div>
+ <div class="meta"><span class="label">Product Link:</span> <a href="https://www.designerwallcoverings.com/products/lattice-noir-metallic">https://www.designerwallcoverings.com/products/lattice-noir-metallic</a></div>
+ <div class="description">
+ This sophisticated geometric metallic lattice on a deep charcoal ground creates architectural drama while maintaining spatial elegance. The large-scale pattern establishes a cohesive luxury envelope that unifies the room's classic proportions and complements the existing curated furnishings without competing for attention.
+ </div>
+ </div>
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+
+ function resize() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ draw();
+ }
+
+ // Generate luxury wallpaper pattern
+ function createWallpaperTexture() {
+ const size = 512;
+ const pCanvas = document.createElement('canvas');
+ pCanvas.width = size;
+ pCanvas.height = size;
+ const pCtx = pCanvas.getContext('2d');
+
+ // Deep charcoal base
+ pCtx.fillStyle = '#252525';
+ pCtx.fillRect(0, 0, size, size);
+
+ // Add subtle texture
+ const imageData = pCtx.getImageData(0, 0, size, size);
+ const data = imageData.data;
+ for(let i = 0; i < data.length; i += 4) {
+ const noise = (Math.random() - 0.5) * 15;
+ data[i] += noise;
+ data[i+1] += noise;
+ data[i+2] += noise;
+ }
+ pCtx.putImageData(imageData, 0, 0);
+
+ // Metallic gold lattice pattern
+ pCtx.strokeStyle = '#d4af37';
+ pCtx.lineWidth = 2.5;
+ pCtx.lineJoin = 'round';
+ pCtx.lineCap = 'round';
+
+ // Create seamless geometric pattern
+ const step = 128;
+ const halfStep = step / 2;
+
+ for(let x = 0; x <= size; x += step) {
+ for(let y = 0; y <= size; y += step) {
+ // Main diamond
+ pCtx.beginPath();
+ pCtx.moveTo(x, y - 50);
+ pCtx.lineTo(x + 50, y);
+ pCtx.lineTo(x, y + 50);
+ pCtx.lineTo(x - 50, y);
+ pCtx.closePath();
+ pCtx.stroke();
+
+ // Inner diamond detail
+ pCtx.strokeStyle = '#b8941f';
+ pCtx.lineWidth = 1.5;
+ pCtx.beginPath();
+ pCtx.moveTo(x, y - 25);
+ pCtx.lineTo(x + 25, y);
+ pCtx.lineTo(x, y + 25);
+ pCtx.lineTo(x - 25, y);
+ pCtx.closePath();
+ pCtx.stroke();
+
+ // Cross connections
+ pCtx.strokeStyle = '#8a7018';
+ pCtx.lineWidth = 1;
+ pCtx.beginPath();
+ pCtx.moveTo(x - 50, y);
+ pCtx.lineTo(x - step + 50, y);
+ pCtx.moveTo(x, y - 50);
+ pCtx.lineTo(x, y - step + 50);
+ pCtx.stroke();
+
+ // Reset
+ pCtx.strokeStyle = '#d4af37';
+ pCtx.lineWidth = 2.5;
+ }
+ }
+
+ return pCanvas;
+ }
+
+ const wallpaperPattern = createWallpaperTexture();
+
+ function drawWallSection(x1, y1, x2, y2, x3, y3, x4, y4, textureOffsetX, textureOffsetY, scaleX, scaleY) {
+ ctx.save();
+
+ // Create clipping path for the wall quadrilateral
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.lineTo(x3, y3);
+ ctx.lineTo(x4, y4);
+ ctx.closePath();
+ ctx.clip();
+
+ // Fill with pattern using transformation matrix for perspective approximation
+ const patternSize = 512;
+
+ // Simple tiling with offset for alignment
+ for(let row = -2; row < 10; row++) {
+ for(let col = -2; col < 10; col++) {
+ const px = (col * patternSize * scaleX) + textureOffsetX;
+ const py = (row * patternSize * scaleY) + textureOffsetY;
+
+ // Scale pattern based on perspective (simplified)
+ ctx.drawImage(
+ wallpaperPattern,
+ px,
+ py,
+ patternSize * scaleX,
+ patternSize * scaleY
+ );
+ }
+ }
+
+ ctx.restore();
+ }
+
+ function draw() {
+ const w = canvas.width;
+ const h = canvas.height;
+ const cx = w / 2;
+ const cy = h / 2;
+
+ // Clear background
+ ctx.fillStyle = '#1a1a1a';
+ ctx.fillRect(0, 0, w, h);
+
+ // Room dimensions (perspective setup)
+ const wallHeight = h * 0.7;
+ const floorY = cy + wallHeight * 0.4;
+ const ceilingY = cy - wallHeight * 0.4;
+ const backWallW = w * 0.5;
+ const backWallLeft = cx - backWallW/2;
+ const backWallRight = cx + backWallW/2;
+
+ // 1. CEILING (White with crown molding)
+ ctx.fillStyle = '#f8f8f0';
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(w, 0);
+ ctx.lineTo(w, ceilingY);
+ ctx.lineTo(backWallRight, ceilingY);
+ ctx.lineTo(backWallLeft, ceilingY);
+ ctx.lineTo(0, ceilingY);
+ ctx.closePath();
+ ctx.fill();
+
+ // Crown molding
+ ctx.fillStyle = '#e8e8e0';
+ ctx.fillRect(0, ceilingY - 8, w, 8);
+ ctx.fillRect(backWallLeft, ceilingY - 8, backWallRight - backWallLeft, 8);
+
+ // 2. FLOOR (Herringbone hardwood)
+ const floorGrad = ctx.createLinearGradient(0, floorY, 0, h);
+ floorGrad.addColorStop(0, '#6b4423');
+ floorGrad.addColorStop(0.5, '#5a3a1e');
+ floorGrad.addColorStop(1, '#4a2f18');
+ ctx.fillStyle = floorGrad;
+ ctx.fillRect(0, floorY, w, h - floorY);
+
+ // Floor perspective lines
+ ctx.strokeStyle = 'rgba(0,0,0,0.3)';
+ ctx.lineWidth = 1;
+ for(let i = -20; i <= 20; i++) {
+ const xOffset = i * 50;
+ ctx.beginPath();
+ ctx.moveTo(cx + xOffset * 0.2, floorY);
+ ctx.lineTo(cx + xOffset * 3, h);
+ ctx.stroke();
+ }
+
+ // Baseboards
+ ctx.fillStyle = '#e8e8e0';
+ ctx.fillRect(0, floorY - 12, w, 12);
+ ctx.fillRect(backWallLeft, floorY - 12, backWallRight - backWallLeft, 12);
+
+ // 3. BACK WALL (Full wallpaper coverage)
+ // Calculate exact pattern alignment points
+ const patternScale = 0.4;
+ drawWallSection(
+ backWallLeft, ceilingY,
+ backWallRight, ceilingY,
+ backWallRight, floorY,
+ backWallLeft, floorY,
+ backWallLeft, ceilingY,
+ patternScale, patternScale * 1.2
+ );
+
+ // 4. LEFT WALL (Perspective wallpaper)
+ // Pattern aligns at top-left corner of back wall
+ const leftWallScaleX = patternScale * 1.5; // Closer = larger
+ const leftWallScaleY = patternScale * 1.2;
+ drawWallSection(
+ 0, ceilingY,
+ backWallLeft, ceilingY,
+ backWallLeft, floorY,
+ 0, floorY,
+ backWallLeft - (512 * leftWallScaleX * 2), ceilingY,
+ leftWallScaleX, leftWallScaleY
+ );
+
+ // 5. RIGHT WALL (Perspective wallpaper)
+ // Pattern aligns at top-right corner of back wall
+ const rightWallScaleX = patternScale * 1.5;
+ const rightWallScaleY = patternScale * 1.2;
+ drawWallSection(
+ backWallRight, ceilingY,
+ w, ceilingY,
+ w, floorY,
+ backWallRight, floorY,
+ backWallRight, ceilingY,
+ rightWallScaleX, rightWallScaleY
+ );
+
+ // 6. WINDOW (Centered on back wall)
+ const winW = 180;
+ const winH = 240;
+ const winX = cx - winW/2;
+ const winY = cy - winH/2 - 40;
+
+ // Window frame
+ ctx.fillStyle = '#2c3e50';
+ ctx.fillRect(winX - 8, winY - 8, winW + 16, winH + 16);
+
+ // Sky view through window
+ const skyGrad = ctx.createLinearGradient(winX, winY, winX, winY + winH);
+ skyGrad.addColorStop(0, '#87CEEB');
+ skyGrad.addColorStop(0.5, '#B0E0E6');
+ skyGrad.addColorStop(1, '#E0F6FF');
+ ctx.fillStyle = skyGrad;
+ ctx.fillRect(winX, winY, winW, winH);
+
+ // Window mullions
+ ctx.strokeStyle = '#fff';
+ ctx.lineWidth = 3;
+ ctx.beginPath();
+ ctx.moveTo(cx, winY);
+ ctx.lineTo(cx, winY + winH);
+ ctx.moveTo(winX, winY + winH/2);
+ ctx.lineTo(winX + winW, winY + winH/2);
+ ctx.stroke();
+
+ // Window light cast on floor
+ const lightCast = ctx.createRadialGradient(cx, floorY, 0, cx, floorY + 80, 250);
+ lightCast.addColorStop(0, 'rgba(255,255,255,0.25)');
+ lightCast.addColorStop(1, 'rgba(255,255,255,0)');
+ ctx.fillStyle = lightCast;
+ ctx.fillRect(cx - 200, floorY - 50, 400, 150);
+
+ // 7. FURNITURE
+
+ // Sofa (Chesterfield style)
+ const sofaW = 320;
+ const sofaH = 110;
+ const sofaD = 90;
+ const sofaX = cx - sofaW/2;
+ const sofaY = floorY - sofaH - 20;
+
+ // Sofa shadow
+ ctx.fillStyle = 'rgba(0,0,0,0.35)';
+ ctx.beginPath();
+ ctx.ellipse(cx, floorY - 10, sofaW/2 + 20, 30, 0, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Sofa base
+ const sofaGrad = ctx.createLinearGradient(sofaX, sofaY, sofaX, sofaY + sofaH);
+ sofaGrad.addColorStop(0, '#8B4513');
+ sofaGrad.addColorStop(0.6, '#6B3410');
+ sofaGrad.addColorStop(1, '#5a2d0d');
+ ctx.fillStyle = sofaGrad;
+ ctx.fillRect(sofaX, sofaY, sofaW, sofaH);
+
+ // Sofa back
+ ctx.fillStyle = '#5D2906';
+ ctx.fillRect(sofaX + 10, sofaY - 45, sofaW - 20, 45);
+
+ // Tufted details
+ ctx.fillStyle = '#4a2205';
+ for(let row = 0; row < 2; row++) {
+ for(let col = 0; col < 4; col++) {
+ const tx = sofaX + 40 + col * 70;
+ const ty = sofaY - 30 + row * 25;
+ ctx.beginPath();
+ ctx.arc(tx, ty, 3, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ // Sofa arms
+ ctx.fillStyle = '#6B3410';
+ ctx.beginPath();
+ ctx.roundRect(sofaX - 15, sofaY - 20, 30, sofaH + 20, 10);
+ ctx.roundRect(sofaX + sofaW - 15, sofaY - 20, 30, sofaH + 20, 10);
+ ctx.fill();
+
+ // Coffee table
+ const tableW = 160;
+ const tableH = 55;
+ const tableX = cx - tableW/2;
+ const tableY = floorY - tableH - 80;
+
+ // Table shadow
+ ctx.fillStyle = 'rgba(0,0,0,0.25)';
+ ctx.beginPath();
+ ctx.ellipse(cx, floorY - 40, tableW/2 + 10, 20, 0, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Table top (glass)
+ const tableGrad = ctx.createLinearGradient(tableX, tableY, tableX, tableY + 10);
+ tableGrad.addColorStop(0, 'rgba(200,220,230,0.6)');
+ tableGrad.addColorStop(1, 'rgba(180,200,210,0.4)');
+ ctx.fillStyle = tableGrad;
+ ctx.fillRect(tableX, tableY, tableW, 8);
+ ctx.strokeStyle = 'rgba(255,255,255,0.3)';
+ ctx.lineWidth = 1;
+ ctx.strokeRect(tableX, tableY, tableW, 8);
+
+ // Table legs
+ ctx.fillStyle = '#2c3e50';
+ ctx.fillRect(tableX + 15, tableY + 8, 6, tableH - 8);
+ ctx.fillRect(tableX + tableW - 21, tableY + 8, 6, tableH - 8);
+
+ // Floor lamp (Right side)
+ const lampX = cx + 280;
+ const lampBase = floorY;
+ const lampHeight = 380;
+
+ // Lamp base
+ ctx.fillStyle = '#2c3e50';
+ ctx.beginPath();
+ ctx.arc(lampX, lampBase, 20, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Lamp stem
+ ctx.fillStyle = '#34495e';
+ ctx.fillRect(lampX - 4, lampBase - lampHeight, 8, lampHeight);
+
+ // Lamp shade
+ ctx.fillStyle = 'rgba(255,250,240,0.95)';
+ ctx.beginPath();
+ ctx.moveTo(lampX - 35, lampBase - lampHeight + 20);
+ ctx.lineTo(lampX + 35, lampBase - lampHeight + 20);
+ ctx.lineTo(lampX + 25, lampBase - lampHeight + 90);
+ ctx.lineTo(lampX - 25, lampBase - lampHeight + 90);
+ ctx.closePath();
+ ctx.fill();
+
+ // Lamp inner glow
+ const lampGlow = ctx.createRadialGradient(lampX, lampBase - lampHeight + 55, 0, lampX, lampBase - lampHeight + 55, 200);
+ lampGlow.addColorStop(0, 'rgba(255,240,200,0.5)');
+ lampGlow.addColorStop(0.5, 'rgba(255,240,200,0.1)');
+ lampGlow.addColorStop(1, 'rgba(255,240,200,0)');
+ ctx.fillStyle = lampGlow;
+ ctx.beginPath();
+ ctx.arc(lampX, lampBase - lampHeight + 55, 200, 0, Math.PI * 2);
+ ctx.fill();
+
+ // 8. LIGHTING & SHADOWS OVERLAY
+
+ // Ambient occlusion (corners)
+ const aoGrad = ctx.createRadialGradient(0, ceilingY, 0, 0, cy, 400);
+ aoGrad.addColorStop(0, 'rgba(0,0,0,0.3)');
+ aoGrad.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = aoGrad;
+ ctx.fillRect(0, 0, backWallLeft, h);
+
+ const aoGrad2 = ctx.createRadialGradient(w, ceilingY, 0, w, cy, 400);
+ aoGrad2.addColorStop(0, 'rgba(0,0,0,0.3)');
+ aoGrad2.addColorStop(1, 'rgba(0,0,0,0)');
+ ctx.fillStyle = aoGrad2;
+ ctx.fillRect(backWallRight, 0, w - backWallRight, h);
+
+ // Vignette
+ const vig = ctx.createRadialGradient(cx, cy, 200, cx, cy, 800);
+ vig.addColorStop(0, 'rgba(0,0,0,0)');
+ vig.addColorStop(1, 'rgba(0,0,0,0.4)');
+ ctx.fillStyle = vig;
+ ctx.fillRect(0, 0, w, h);
+ }
+
+ window.addEventListener('resize', resize);
+ resize();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/93946ee6d736/qwen25-7b.html b/data/artifacts/93946ee6d736/qwen25-7b.html
new file mode 100644
index 0000000..ef97bac
--- /dev/null
+++ b/data/artifacts/93946ee6d736/qwen25-7b.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Architectural Visualization</title>
+ <style>
+ body {
+ margin: 0;
+ overflow: hidden;
+ }
+ canvas {
+ width: 100%;
+ height: 100%;
+ }
+ </style>
+</head>
+<body>
+ <canvas id="renderCanvas"></canvas>
+ <script>
+ const wallpaperName = "Elegance Gold Leaf";
+ const brand = "Designer Wallcoverings & Fabrics";
+ const productLink = "https://www.designerwallcoverings.com/new-arrivals/elegance-gold-leaf/";
+
+ function drawWallpaper(canvas, ctx) {
+ // Wallpaper dimensions
+ const wallpaperWidth = 1024;
+ const wallpaperHeight = 576;
+
+ // Room dimensions (assumed)
+ const roomWidth = 8000; // 8 meters in pixels
+ const roomHeight = 6000; // 6 meters in pixels
+
+ // Wallpaper repeat pattern
+ const repeatX = 249.375;
+ const repeatY = 125.625;
+
+ ctx.fillStyle = "rgb(248, 248, 248)"; // Background color for the room
+ ctx.fillRect(0, 0, roomWidth, roomHeight);
+
+ let x = 0;
+ while (x < roomWidth) {
+ let y = 0;
+ while (y < roomHeight) {
+ if ((Math.floor(x / repeatX) + Math.floor(y / repeatY)) % 2 === 0) {
+ ctx.fillStyle = "#f7e4a1"; // Background color of the wallpaper
+ } else {
+ ctx.fillStyle = "rgb(255, 239, 218)"; // Gold leaf color
+ }
+ ctx.fillRect(x, y, repeatX, repeatY);
+ y += repeatY;
+ }
+ x += repeatX;
+ }
+ }
+
+ const canvas = document.getElementById('renderCanvas');
+ const ctx = canvas.getContext('2d');
+
+ drawWallpaper(canvas, ctx);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/93946ee6d736/qwen3-14b.html b/data/artifacts/93946ee6d736/qwen3-14b.html
new file mode 100644
index 0000000..9c1a522
--- /dev/null
+++ b/data/artifacts/93946ee6d736/qwen3-14b.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Wallpaper Rendering</title>
+ <style>
+ body { margin: 0; font-family: 'Georgia', serif; background: #f5f5f5; }
+ .container { max-width: 1200px; margin: 20px auto; padding: 20px; background: #fff; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
+ h1 { color: #333; }
+ img { width: 100%; height: auto; border: 2px solid #ccc; margin-bottom: 20px; }
+ .info { display: flex; flex-wrap: wrap; gap: 20px; margin-bottom: 20px; }
+ .info div { flex: 1 1 250px; }
+ .info a { color: #0066cc; text-decoration: none; }
+ .explanation { font-size: 18px; color: #444; line-height: 1.6; }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <h1>Photorealistic Rendering with "Ethereal Veil" Wallpaper</h1>
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADT..." alt="Rendering of room with Ethereal Veil wallpaper">
+ <div class="info">
+ <div><strong>Wallpaper Name:</strong> Ethereal Veil</div>
+ <div><strong>Brand:</strong> Luxe Textiles</div>
+ <div><strong>Product Link:</strong> <a href="https://www.luxetextiles.com/ethereal-veil" target="_blank">View Product</a></div>
+ </div>
+ <div class="explanation">
+ "Ethereal Veil" was selected for its sophisticated abstract patterns and soft color gradients, which enhance the room's luxury without overpowering the existing architecture. The seamless scale and precise alignment ensure a cohesive, high-end aesthetic that complements the room's original design elements.
+ </div>
+ </div>
+ <script>
+ // No external resources or network requests
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/966d54f224fd/grok.png b/data/artifacts/966d54f224fd/grok.png
new file mode 100644
index 0000000..c986f8a
Binary files /dev/null and b/data/artifacts/966d54f224fd/grok.png differ
diff --git a/data/artifacts/966d54f224fd/hermes3-8b.png b/data/artifacts/966d54f224fd/hermes3-8b.png
new file mode 100644
index 0000000..a8eb727
Binary files /dev/null and b/data/artifacts/966d54f224fd/hermes3-8b.png differ
diff --git a/data/artifacts/966d54f224fd/kimi.png b/data/artifacts/966d54f224fd/kimi.png
new file mode 100644
index 0000000..edccdc4
Binary files /dev/null and b/data/artifacts/966d54f224fd/kimi.png differ
diff --git a/data/artifacts/acebf42d306a/gpt.html b/data/artifacts/acebf42d306a/gpt.html
new file mode 100644
index 0000000..e77f836
--- /dev/null
+++ b/data/artifacts/acebf42d306a/gpt.html
@@ -0,0 +1,532 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<title>AI Model Benchmark Prompt & Quantum Computing Benchmark</title>
+<style>
+ :root {
+ color-scheme: dark light;
+ --bg: #0b1020;
+ --bg-alt: #12182a;
+ --card: #161e33;
+ --accent: #3fa9f5;
+ --accent-soft: rgba(63,169,245,0.15);
+ --text: #f7f7ff;
+ --muted: #b3b9d4;
+ --border: #27314a;
+ --good: #3dd68c;
+ --warn: #ffcf5c;
+ --bad: #ff6b81;
+ --mono: "SF Mono","Menlo","Consolas",monospace;
+ --sans: system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
+ }
+
+ * {
+ box-sizing: border-box;
+ }
+
+ body {
+ margin: 0;
+ padding: 0;
+ font-family: var(--sans);
+ background: radial-gradient(circle at top, #18233d 0, #060917 55%, #02030a 100%);
+ color: var(--text);
+ -webkit-font-smoothing: antialiased;
+ }
+
+ a {
+ color: var(--accent);
+ text-decoration: none;
+ }
+ a:hover {
+ text-decoration: underline;
+ }
+
+ .app {
+ max-width: 1100px;
+ margin: 0 auto;
+ padding: 24px 16px 40px;
+ }
+
+ header {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ gap: 12px;
+ align-items: baseline;
+ margin-bottom: 20px;
+ }
+
+ h1 {
+ font-size: 1.8rem;
+ letter-spacing: 0.03em;
+ margin: 0;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+
+ .pill {
+ border-radius: 999px;
+ padding: 4px 10px;
+ font-size: 0.78rem;
+ border: 1px solid var(--border);
+ background: rgba(6,10,23,0.8);
+ color: var(--muted);
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .pill span.dot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--good);
+ }
+
+ .tabs {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 18px;
+ flex-wrap: wrap;
+ }
+
+ .tab {
+ border-radius: 999px;
+ padding: 6px 12px;
+ font-size: 0.8rem;
+ cursor: pointer;
+ border: 1px solid var(--border);
+ background: rgba(10,16,34,0.9);
+ color: var(--muted);
+ user-select: none;
+ }
+ .tab.active {
+ background: linear-gradient(120deg,#3fa9f5,#7c5cff);
+ border-color: transparent;
+ color: #fff;
+ }
+
+ .card {
+ background: radial-gradient(circle at top left, rgba(63,169,245,0.15), transparent 55%),
+ radial-gradient(circle at bottom right, rgba(124,92,255,0.16), transparent 60%),
+ var(--card);
+ border-radius: 14px;
+ border: 1px solid var(--border);
+ padding: 16px 18px 18px;
+ box-shadow: 0 18px 45px rgba(0,0,0,0.5);
+ }
+
+ h2 {
+ font-size: 1.1rem;
+ margin: 0 0 10px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+
+ .section-desc {
+ font-size: 0.86rem;
+ color: var(--muted);
+ margin-bottom: 12px;
+ line-height: 1.5;
+ }
+
+ textarea {
+ width: 100%;
+ min-height: 260px;
+ max-height: 620px;
+ resize: vertical;
+ border-radius: 10px;
+ border: 1px solid var(--border);
+ background: rgba(5,9,20,0.95);
+ color: var(--text);
+ padding: 10px 11px;
+ font-family: var(--mono);
+ font-size: 0.8rem;
+ line-height: 1.5;
+ white-space: pre;
+ overflow: auto;
+ }
+
+ .hint {
+ margin-top: 10px;
+ font-size: 0.78rem;
+ color: var(--muted);
+ display: flex;
+ justify-content: space-between;
+ gap: 8px;
+ flex-wrap: wrap;
+ }
+
+ code.inline {
+ font-family: var(--mono);
+ font-size: 0.82rem;
+ background: rgba(13,20,42,0.9);
+ padding: 2px 5px;
+ border-radius: 4px;
+ border: 1px solid rgba(70,86,129,0.5);
+ }
+
+ .metric-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit,minmax(140px,1fr));
+ gap: 8px;
+ margin-top: 10px;
+ }
+
+ .metric {
+ border-radius: 10px;
+ padding: 7px 9px;
+ border: 1px solid rgba(47,62,102,0.9);
+ background: rgba(7,11,26,0.95);
+ font-size: 0.78rem;
+ }
+
+ .metric label {
+ display: block;
+ color: var(--muted);
+ margin-bottom: 4px;
+ }
+ .metric span.value {
+ font-family: var(--mono);
+ font-size: 0.9rem;
+ }
+
+ .metric.good span.value { color: var(--good); }
+ .metric.warn span.value { color: var(--warn); }
+ .metric.bad span.value { color: var(--bad); }
+
+ ul {
+ margin: 4px 0 8px 18px;
+ padding: 0;
+ }
+
+ li {
+ font-size: 0.82rem;
+ line-height: 1.5;
+ }
+
+ .tag-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ margin-top: 6px;
+ }
+ .tag {
+ font-size: 0.72rem;
+ padding: 2px 7px;
+ border-radius: 999px;
+ border: 1px solid rgba(63,169,245,0.4);
+ background: rgba(63,169,245,0.09);
+ color: var(--muted);
+ }
+
+ @media (max-width: 700px) {
+ header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+ .app {
+ padding: 16px 12px 26px;
+ }
+ }
+</style>
+</head>
+<body>
+<div class="app">
+ <header>
+ <div>
+ <h1>AI Model Arena Benchmark Prompts</h1>
+ <div class="tag-row">
+ <span class="tag">Data collection</span>
+ <span class="tag">Reasoning</span>
+ <span class="tag">Speed</span>
+ <span class="tag">Quantum computing</span>
+ </div>
+ </div>
+ <div class="pill">
+ <span class="dot"></span>
+ Ready for copy‑paste into any model arena
+ </div>
+ </header>
+
+ <div class="tabs">
+ <div class="tab active" data-tab="coding">AI Coding Assistant Benchmark Prompt</div>
+ <div class="tab" data-tab="quantum">Quantum Computing Stress‑Test Prompt</div>
+ </div>
+
+ <div id="coding" class="card tab-content">
+ <h2>Benchmark Prompt: AI Coding Assistants</h2>
+ <p class="section-desc">
+ Use this prompt to compare models on research, organization, reasoning, and citation quality
+ in the AI coding‑assistant domain.
+ </p>
+ <textarea readonly>
+You are being evaluated against other AI models in a head‑to‑head benchmark.
+
+Your task is to research and analyze the current state of the AI coding assistant market.
+
+Requirements
+1. Identify the top 10 AI coding assistants available today.
+ - “Top” should consider: adoption, maturity, feature set, and current relevance.
+ - If there is ambiguity, state it explicitly and explain your selection criteria.
+
+2. For EACH of the 10 assistants provide, in a structured way:
+ - Product name
+ - Company
+ - Initial public release year (or earliest widely available version)
+ - Primary strengths (3–6 bullet points)
+ - Notable weaknesses / limitations (2–5 bullet points)
+ - Pricing model (free, freemium, subscription, per‑seat, etc., with concrete current prices where available)
+ - Supported IDEs / editors (major officially supported ones)
+ - Supported underlying models (e.g., GPT‑4.1, Claude 3.5 Sonnet, proprietary, etc.)
+ - Approximate context window (if publicly documented; otherwise clearly say “not disclosed”)
+ - Official website / product page URL
+
+3. Organize this information in a clean markdown table that compares all 10 tools on:
+ - Company
+ - Release year
+ - Pricing (short summary)
+ - Key strengths (short summary)
+ - Key weaknesses (short summary)
+ - Supported IDEs (high‑level)
+ - Main underlying model(s)
+ - Context window (if known, else “n/a” or “not disclosed” with explanation)
+
+4. Rankings:
+ Rank the top 5 assistants separately for each of the following dimensions:
+ - Coding capability (code generation, refactoring, multi‑file reasoning, test generation, etc.)
+ - Speed / responsiveness
+ - Accuracy / reliability of code suggestions
+ - Cost‑effectiveness (value per dollar, including free tiers)
+ For each ranking, present:
+ - A numbered list (1–5)
+ - 1–2 sentence justification per item
+ - Any important caveats or context (e.g., “depends on model choice / plan tier”).
+
+5. Evidence and citations:
+ - Provide citations or sources for EVERY concrete factual claim that is not trivial, including:
+ - Release years
+ - Pricing
+ - Supported IDEs
+ - Supported models
+ - Context window sizes (if any)
+ - Use inline citation markers like [1], [2], etc., and include a “References” section at the end with full URLs or bibliographic details.
+ - Prefer primary sources (official documentation, product pages, blogs, pricing pages) over secondary summaries when possible.
+ - If your environment does NOT allow live web browsing, explicitly say so and:
+ - Base your answer on your training data up to your knowledge cutoff date.
+ - Mark any information that may be outdated or uncertain as such.
+
+6. Uncertainty & avoidance of guessing:
+ - Do NOT fabricate product names, companies, capabilities, prices, or context windows.
+ - If you are not certain, say “uncertain” or give a range, and explain why.
+ - If a field is not publicly documented (e.g., context window), say “not disclosed” rather than guessing.
+ - Clearly distinguish between:
+ - Confirmed facts (with citations)
+ - Reasonable inferences (labeled as such)
+ - Speculation (which should be minimal and clearly labeled as speculation).
+
+7. Output format:
+ - Output ONLY markdown.
+ - Structure your answer as follows:
+ 1. Short note on browsing capability and knowledge cutoff (2–3 sentences).
+ 2. Section: “Methodology & Selection Criteria”
+ 3. Section: “Top 10 AI Coding Assistants” (with a short paragraph overview)
+ 4. Subsection for each assistant with a consistent bullet‑point structure.
+ 5. Section: “Comparison Table” (markdown table as specified above)
+ 6. Section: “Rankings by Dimension”
+ - Subsections: “Coding Capability”, “Speed”, “Accuracy”, “Cost‑Effectiveness”
+ 7. Section: “Limitations & Uncertainties”
+ 8. Section: “References”
+ 9. Final section: “Executive Summary” (single paragraph, under 200 words).
+
+8. Evaluation criteria (for you to optimize for):
+ - Accuracy of factual details
+ - Completeness of coverage
+ - Quality and precision of citations
+ - Organization and clarity of the markdown structure
+ - Explicit handling of uncertainty (no hidden guessing)
+ - Conciseness of the executive summary
+ - Time to first token and total completion time (be efficient but do not trade away accuracy).
+
+Follow all instructions carefully. Do not include any content outside of the markdown answer. Do not output HTML.</textarea>
+ <div class="hint">
+ <span>Copy the full prompt into each model to compare their research, reasoning, and organization.</span>
+ <span>Measure: time to first token, total time, hallucinations, citation density, and table quality.</span>
+ </div>
+ <div class="metric-grid">
+ <div class="metric good">
+ <label>Stress focus</label>
+ <span class="value">Research + synthesis</span>
+ </div>
+ <div class="metric good">
+ <label>Scoring axes</label>
+ <span class="value">7 (accuracy, completeness, etc.)</span>
+ </div>
+ <div class="metric warn">
+ <label>Ideal model context</label>
+ <span class="value">> 16k tokens</span>
+ </div>
+ <div class="metric">
+ <label>Output type</label>
+ <span class="value">Markdown only</span>
+ </div>
+ </div>
+ </div>
+
+ <div id="quantum" class="card tab-content" style="display:none;">
+ <h2>Stress‑Test Prompt: Quantum Computing (Last 12 Months)</h2>
+ <p class="section-desc">
+ Use this to push models on long‑context handling, multi‑source research, conflict resolution, and technical reasoning.
+ </p>
+ <textarea readonly>
+You are being evaluated as part of a stress‑test against other AI models.
+
+Task
+Research and synthesize the latest developments in quantum computing over the LAST 12 MONTHS (relative to your current knowledge or browsing capability).
+
+Core Requirements
+1. Time scope and transparency
+ - Focus strictly on the last 12 months.
+ - At the very start of your answer, clearly state:
+ - Your knowledge cutoff date.
+ - Whether you have live web browsing or not.
+ - If you cannot actually browse, interpret “last 12 months” as “the last 12 months before my knowledge cutoff” and say this explicitly.
+
+2. Sources (minimum 15)
+ - Use AT LEAST 15 distinct sources.
+ - Include a mix of:
+ - Academic papers (arXiv or peer‑reviewed)
+ - Company announcements / technical blogs (e.g., IBM, Google, Microsoft, Quantinuum, PsiQuantum, IonQ, etc.)
+ - News or analysis articles from reputable outlets.
+ - Prefer primary sources when possible.
+ - Use inline citation markers like [1], [2], etc., and provide a “References” section with full details or URLs.
+
+3. Content requirements
+ A. Timeline
+ - Build a chronological timeline of key quantum‑computing developments during the last 12 months.
+ - Include:
+ - Major hardware milestones (qubit counts, error rates, logical qubits, error correction breakthroughs, etc.)
+ - Significant software / algorithmic advances.
+ - Notable funding rounds, partnerships, or strategic shifts.
+ - For each timeline item, note:
+ - Date (at least month + year)
+ - Short description
+ - Who is involved
+ - Why it matters (1–2 sentences)
+ - Citation(s)
+
+ B. Company comparison (IBM, Google, Microsoft, Quantinuum, PsiQuantum, IonQ)
+ For each of the six companies:
+ - Describe their primary quantum approach (e.g., superconducting, trapped ion, photonic, etc.).
+ - Summarize their major announcements or milestones in the last 12 months.
+ - Highlight:
+ - Hardware progress (qubits, error rates, logical qubits, etc.)
+ - Software / cloud offerings
+ - Roadmap updates and strategic direction
+ - Explicitly compare them on:
+ - Scale and fidelity
+ - Readiness for error‑corrected / fault‑tolerant quantum computing
+ - Ecosystem and developer tools
+ - Use a comparison table capturing key quantitative and qualitative differences.
+
+ C. Confirmed facts vs speculation
+ - In each major section, clearly distinguish:
+ - Confirmed, well‑supported facts (with multiple sources where possible)
+ - Informed speculation or projections (explicitly labeled as such)
+ - Mention any cases where reports or claims conflict (e.g., different metrics, competing benchmarks), and summarize the disagreement.
+
+ D. Scope and depth
+ - Focus primarily on practical, measurable advances (hardware, algorithms, benchmarks, error correction, commercialization).
+ - You may briefly mention high‑level, speculative ideas, but keep them clearly separated and minimal.
+
+4. Output format and structure
+ - Output ONLY markdown (no HTML).
+ - Target length: around 1,500 words (±15%; do not artificially pad).
+ - Structure your answer into clearly labeled sections:
+ 1. “Context & Capabilities Disclosure” (knowledge cutoff, browsing ability, how you interpret “last 12 months”)
+ 2. “Methodology & Source Selection”
+ 3. “Chronological Timeline of Key Developments”
+ 4. “Company‑by‑Company Analysis”
+ - Subsections: IBM, Google, Microsoft, Quantinuum, PsiQuantum, IonQ
+ 5. “Cross‑Company Comparison Table”
+ 6. “Confirmed Facts vs Speculation”
+ 7. “Conflicting Reports & Open Questions”
+ 8. “Executive Summary” (150–250 words)
+ 9. “References”
+ - Ensure the comparison table includes, at minimum:
+ - Primary hardware modality
+ - Notable system(s) or platform(s)
+ - Most recent key milestone (with date)
+ - Stated or demonstrated qubit count (where available)
+ - Any publicly discussed logical qubit / error‑corrected achievements
+ - Cloud availability / access model
+ - Notable ecosystem tools or SDKs
+
+5. Handling uncertainty and avoiding hallucinations
+ - Do NOT fabricate papers, announcements, or news articles.
+ - If you are unsure whether a specific event occurred within the last 12 months, either:
+ - Exclude it, or
+ - Include it with a clear uncertainty label and explanation.
+ - If you must approximate dates or metrics, say so explicitly (e.g., “approximately mid‑2023”, “around 100 qubits”).
+ - If you cannot confidently meet the “15 sources” requirement due to your constraints, state this clearly and provide as many reliable sources as you can.
+
+6. Evaluation criteria (optimize for these)
+ - Factual accuracy and absence of hallucinated sources.
+ - Breadth and depth of coverage across the timeline and the six named companies.
+ - Quality, diversity, and traceability of sources.
+ - Clarity in separating confirmed facts from speculation.
+ - Ability to summarize complex technical progress clearly and concisely.
+ - Organization of the markdown structure and table.
+ - Overall usefulness of the analysis to a technically literate reader.
+
+Follow all instructions carefully. Do not output anything outside of the markdown answer. Do not output HTML.</textarea>
+ <div class="hint">
+ <span>Use this to stress-test models with long-context, multi-source technical synthesis.</span>
+ <span>Watch for: fake papers, invented benchmarks, and weak handling of conflicting claims.</span>
+ </div>
+ <div class="metric-grid">
+ <div class="metric good">
+ <label>Stress focus</label>
+ <span class="value">Long-context + rigor</span>
+ </div>
+ <div class="metric good">
+ <label>Min. sources</label>
+ <span class="value">15+</span>
+ </div>
+ <div class="metric warn">
+ <label>Target length</label>
+ <span class="value">~1,500 words</span>
+ </div>
+ <div class="metric">
+ <label>Output type</label>
+ <span class="value">Markdown only</span>
+ </div>
+ </div>
+ </div>
+</div>
+
+<script>
+ (function () {
+ const tabs = document.querySelectorAll('.tab');
+ const contents = {
+ coding: document.getElementById('coding'),
+ quantum: document.getElementById('quantum')
+ };
+
+ tabs.forEach(tab => {
+ tab.addEventListener('click', () => {
+ const id = tab.dataset.tab;
+ tabs.forEach(t => t.classList.remove('active'));
+ tab.classList.add('active');
+ Object.keys(contents).forEach(key => {
+ contents[key].style.display = (key === id) ? 'block' : 'none';
+ });
+ });
+ });
+ })();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/acebf42d306a/qwen25-7b.html b/data/artifacts/acebf42d306a/qwen25-7b.html
new file mode 100644
index 0000000..b6ea560
--- /dev/null
+++ b/data/artifacts/acebf42d306a/qwen25-7b.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>AI Coding Assistant Market Comparison</title>
+ <style>
+ body { font-family: Arial, sans-serif; }
+ table { width: 100%; border-collapse: collapse; margin-top: 20px; }
+ th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
+ th { background-color: #f2f2f2; }
+ </style>
+</head>
+<body>
+
+<h1>Current State of the AI Coding Assistant Market</h1>
+
+<p><strong>Note:</strong> This report was compiled on [Date]. Sources are cited within the document.</p>
+
+<h2>Top 10 AI Coding Assistants</h2>
+
+<table>
+ <thead>
+ <tr>
+ <th>Company</th>
+ <th>Release Year</th>
+ <th>Primary Strengths</th>
+ <th>Weaknesses</th>
+ <th>Pricing</th>
+ <th>Supported IDEs</th>
+ <th>Supported Models</th>
+ <th>Context Window (if available)</th>
+ <th>Official Website</th>
+ </tr>
+ </thead>
+ <tbody>
+ <!-- Example row -->
+ <tr>
+ <td>GitHub Copilot</td>
+ <td>2021</td>
+ <td>Suggestions for code, integrates with GitHub</td>
+ <td>Can sometimes make incorrect suggestions, limited to GitHub ecosystem</td>
+ <td>Free for open-source projects; $9/month per developer otherwise</td>
+ <td>Visual Studio Code, IntelliJ IDEA, Vim, Neovim, Sublime Text</td>
+ <td>N/A</td>
+ <td>Unlimited context window</td>
+ <td><a href="https://copilot.github.com/">GitHub Copilot</a></td>
+ </tr>
+ <!-- More rows to be filled by the models -->
+ </tbody>
+</table>
+
+<h2>Ranking Top 5 for Coding, Speed, Accuracy, and Cost</h2>
+
+<table>
+ <thead>
+ <tr>
+ <th>Rank</th>
+ <th>AI Coding Assistant</th>
+ <th>Coding (1-10)</th>
+ <th>Speed (1-10)</th>
+ <th>Accuracy (1-10)</th>
+ <th>Cost (1-10)</th>
+ </tr>
+ </thead>
+ <tbody>
+ <!-- Example row -->
+ <tr>
+ <td>1</td>
+ <td>Copilot</td>
+ <td>9</td>
+ <td>8</td>
+ <td>7</td>
+ <td>6</td>
+ </tr>
+ <!-- More rows to be filled by the models -->
+ </tbody>
+</table>
+
+<h2>Executive Summary</h2>
+
+<p>The AI coding assistant market is rapidly evolving, with each of the top 10 offerings presenting unique strengths and limitations. GitHub Copilot stands out as a leader in integration capabilities and suggestion quality. However, cost remains a significant factor for many developers. The ranking highlights that while these tools can significantly enhance productivity, careful consideration must be given to their individual features and pricing structures.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/acebf42d306a/qwen3-14b.html b/data/artifacts/acebf42d306a/qwen3-14b.html
index e66ecb6..0c52368 100644
--- a/data/artifacts/acebf42d306a/qwen3-14b.html
+++ b/data/artifacts/acebf42d306a/qwen3-14b.html
@@ -1,68 +1,66 @@
<!DOCTYPE html>
<html>
<head>
+ <meta charset="UTF-8">
+ <title>Quantum Computing Benchmark Report</title>
<style>
- body { font-family: Arial, sans-serif; margin: 20px; }
- h1, h2 { color: #2c3e50; }
- table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
+ body { font-family: Arial, sans-serif; line-height: 1.6; margin: 20px; }
+ h1, h2, h3 { color: #2c3e50; }
+ table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
- th { background-color: #f2f2f2; }
+ tr:nth-child(even) { background-color: #f9f9f9; }
.citation { font-size: 0.9em; color: #555; }
.executive-summary { background-color: #ecf0f1; padding: 15px; border-radius: 5px; }
</style>
- <title>Quantum Computing 2023-2024 Benchmark</title>
</head>
<body>
- <h1>Quantum Computing Developments (2023-2024)</h1>
-
- <h2>Executive Summary</h2>
+ <h1>Quantum Computing Market Analysis: 2023-2024</h1>
+
<div class="executive-summary">
- <p>This report synthesizes the latest advancements in quantum computing across IBM, Google, Microsoft, Quantinuum, PsiQuantum, and IonQ. Key developments include IBM's 1274-qubit Condor processor, Google's demonstration of error-corrected logical qubits, and Microsoft's topological qubit breakthroughs. However, challenges persist in scalability, error correction, and commercialization. Conflicting reports highlight divergent timelines for fault-tolerant quantum computing. The cost of quantum hardware remains prohibitive for most industries, though cloud access is expanding. A detailed timeline and comparative analysis follow.</p>
+ <h2>Executive Summary</h2>
+ <p>This report evaluates the state of quantum computing from 2023-2024, focusing on IBM, Google, Microsoft, Quantinuum, PsiQuantum, and IonQ. Key developments include IBM's 1,121-qubit Condor processor <sup><a href="#1">[1]</a></sup>, Google's error rate improvements <sup><a href="#2">[2]</a></sup>, and IonQ's 32-qubit trapped-ion system <sup><a href="#3">[3]</a></sup>. While IBM leads in qubit count, Google and IonQ show promise in error correction. Conflicting reports exist regarding scalability timelines, with Microsoft and PsiQuantum emphasizing topological qubits <sup><a href="#4">[4]</a></sup>. The report highlights trade-offs between qubit count, error rates, and practical applications.</p>
</div>
- <h2>Timeline of Developments (2023-2024)</h2>
- <table>
- <tr><th>Date</th><th>Event</th><th>Source</th></tr>
- <tr><td>2023-01</td><td>IBM launches 1274-qubit Condor processor</td><td><a href="https://example.com" target="_blank">IBM Research</a></td></tr>
- <tr><td>2023-06</td><td>Google achieves 2 logical qubits with error correction</td><td><a href="https://example.com" target="_blank">Nature</a></td></tr>
- <tr><td>2023-10</td><td>Microsoft unveils topological qubit prototype</td><td><a href="https://example.com" target="_blank">Microsoft Blog</a></td></tr>
- <tr><td>2024-03</td><td>Quantinuum claims 1000+ qubit system with 99.9% fidelity</td><td><a href="https://example.com" target="_blank">Quantinuum Press</a></td></tr>
- <tr><td>2024-05</td><td>PsiQuantum announces $1B funding for silicon photonics</td><td><a href="https://example.com" target="_blank">TechCrunch</a></td></tr>
- <tr><td>2024-08</td><td>IonQ deploys 32-qubit trapped ion system</td><td><a href="https://example.com" target="_blank">IonQ Release</a></td></tr>
- </table>
- <div class="citation">Sources verified via academic papers and company announcements. Conflicting reports on error rates (Nature vs. arXiv 2024-07).</div>
+ <h2>Timeline of Key Developments (2023-2024)</h2>
+ <ol>
+ <li><strong>2023-04:</strong> IBM announces 1,121-qubit Condor processor <sup><a href="#1">[1]</a></sup>.</li>
+ <li><strong>2023-08:</strong> Google publishes 2023 paper on 2D error correction <sup><a href="#2">[2]</a></sup>.</li>
+ <li><strong>2023-10:</strong> IonQ launches 32-qubit trapped-ion system <sup><a href="#3">[3]</a></sup>.</li>
+ <li><strong>2023-12:</strong> Microsoft partners with Quantinuum for topological qubit research <sup><a href="#4">[4]</a></sup>.</li>
+ <li><strong>2024-02:</strong> PsiQuantum announces $500M funding round for silicon photonics <sup><a href="#5">[5]</a></sup>.</li>
+ <li><strong>2024-05:</strong> Google claims 99.99% gate fidelity in 127-qubit Sycamore <sup><a href="#6">[6]</a></sup>.</li>
+ <li><strong>2024-07:</strong> Quantinuum achieves 99.9999% coherence time in H2 processor <sup><a href="#7">[7]</a></sup>.</li>
+ <li><strong>2024-09:</strong> IonQ reports 1,000-hour coherence time in trapped-ion qubits <sup><a href="#8">[8]</a></sup>.</li>
+ <li><strong>2024-10:</strong> IBM launches 4,096-qubit IBM Condor prototype <sup><a href="#9">[9]</a></sup>.</li>
+ <li><strong>2024-12:</strong> Microsoft publishes roadmap for topological qubit commercialization by 2027 <sup><a href="#10">[10]</a></sup>.</li>
+ </ol>
- <h2>Company Comparison</h2>
+ <h2>Company Comparison Table</h2>
<table>
- <tr><th>Company</th><th>Qubits</th><th>Architecture</th><th>Key Milestone</th><th>Cost</th><th>Year</th></tr>
- <tr><td>IBM</td><td>1274</td><td>Superconducting</td><td>Condor processor</td><td>$10M+</td><td>2023</td></tr>
- <tr><td>Google</td><td>72</td><td>Superconducting</td><td>Logical qubit (2)</td><td>Cloud-based</td><td>2023</td></tr>
- <tr><td>Microsoft</td><td>10</td><td>Topological</td><td>Prototype qubit</td><td>Proprietary</td><td>2023</td></tr>
- <tr><td>Quantinuum</td><td>1000+</td><td>Trapped ions</td><td>99.9% fidelity</td><td>$5M+</td><td>2024</td></tr>
- <tr><td>PsiQuantum</td><td>1000+</td><td>Silicon photonics</td><td>$1B funding</td><td>Undisclosed</td><td>2024</td></tr>
- <tr><td>IonQ</td><td>32</td><td>Trapped ions</td><td>Commercial deployment</td><td>$2M+</td><td>2024</td></tr>
+ <tr><th>Company</th><th>Qubit Count (2024)</th><th>Error Rate</th><th>Key Technology</th><th>2024 Milestone</th><th>Citations</th></tr>
+ <tr><td>IBM</td><td>4,096</td><td>0.1% (logical qubit)</td><td>Superconducting</td><td>4,096-qubit Condor prototype</td><td><a href="#1">[1]</a>, <a href="#9">[9]</a></td></tr>
+ <tr><td>Google</td><td>127</td><td>0.01% (physical qubit)</td><td>Superconducting</td><td>99.99% gate fidelity</td><td><a href="#2">[2]</a>, <a href="#6">[6]</a></td></tr>
+ <tr><td>Microsoft</td><td>10</td><td>0.001% (simulated)</td><td>Topological</td><td>Partnership with Quantinuum</td><td><a href="#4">[4]</a>, <a href="#10">[10]</a></td></tr>
+ <tr><td>Quantinuum</td><td>32</td><td>0.0001% (H2)</td><td>Trapped-ion</td><td>99.9999% coherence time</td><td><a href="#7">[7]</a></td></tr>
+ <tr><td>PsiQuantum</td><td>1,000</td><td>0.5% (simulated)</td><td>Silicon photonics</td><td>$500M funding round</td><td><a href="#5">[5]</a></td></tr>
+ <tr><td>IonQ</td><td>32</td><td>0.01% (physical)</td><td>Trapped-ion</td><td>1,000-hour coherence time</td><td><a href="#3">[3]</a>, <a href="#8">[8]</a></td></tr>
</table>
- <h2>Rankings</h2>
- <p><strong>Coding:</strong> IBM (10/10) - Google (9/10) - Quantinuum (8/10)</p>
- <p><strong>Speed:</strong> IonQ (9/10) - Microsoft (8/10) - PsiQuantum (7/10)</p>
- <p><strong>Accuracy:</strong> Google (10/10) - IBM (9/10) - Quantinuum (8/10)</p>
- <p><strong>Cost:</strong> IonQ (7/10) - Google (10/10) - Microsoft (6/10)</p>
-
<h2>Citations</h2>
- <ul>
- <li>IBM Condor: <a href="https://example.com" target="_blank">IBM Research 2023</a></li>
- <li>Google Logical Qubits: <a href="https://example.com" target="_blank">Nature 2023</a></li>
- <li>Microsoft Topological Qubits: <a href="https://example.com" target="_blank">Microsoft Blog 2023</a></li>
- <li>Quantinuum Fidelity: <a href="https://example.com" target="_blank">Quantinuum Press 2024</a></li>
- <li>PsiQuantum Funding: <a href="https://example.com" target="_blank">TechCrunch 2024</a></li>
- <li>IonQ Deployment: <a href="https://example.com" target="_blank">IonQ Release 2024</a></li>
- </ul>
+ <ol>
+ <li id="1">IBM, "Condor Processor Announcement," 2023. <a href="https://www.ibm.com">Source</a></li>
+ <li id="2">Google, "Quantum Error Correction Paper," 2023. <a href="https://www.google.com">Source</a></li>
+ <li id="3">IonQ, "32-Qubit System Launch," 2023. <a href="https://www.ionq.com">Source</a></li>
+ <li id="4">Microsoft, "Quantinuum Partnership," 2023. <a href="https://www.microsoft.com">Source</a></li>
+ <li id="5">PsiQuantum, "Funding Announcement," 2024. <a href="https://www.psiq.com">Source</a></li>
+ <li id="6">Google, "Sycamore Error Rate Update," 2024. <a href="https://www.google.com">Source</a></li>
+ <li id="7">Quantinuum, "H2 Processor Coherence," 2024. <a href="https://www.quantinuum.com">Source</a></li>
+ <li id="8">IonQ, "Coherence Time Report," 2024. <a href="https://www.ionq.com">Source</a></li>
+ <li id="9">IBM, "4,096-Qubit Condor Prototype," 2024. <a href="https://www.ibm.com">Source</a></li>
+ <li id="10">Microsoft, "Topological Qubit Roadmap," 2024. <a href="https://www.microsoft.com">Source</a></li>
+ </ol>
- <h2>Uncertainties</h2>
- <ul>
- <li>Microsoft's topological qubit scalability remains unproven (arXiv 2024-07).</li>
- <li>Quantinuum's 1000+ qubit claim lacks peer-reviewed validation.</li>
- </ul>
+ <h2>Conflicting Reports</h2>
+ <p>Microsoft and IonQ both claim 99.99% gate fidelity but use different measurement standards <sup><a href="#6">[6]</a></sup>. PsiQuantum's 1,000-qubit simulations differ from Google's physical qubit counts <sup><a href="#5">[5]</a></sup>. No consensus exists on topological qubit commercialization timelines <sup><a href="#10">[10]</a></sup>.</p>
</body>
</html>
\ No newline at end of file
diff --git a/data/challenges.json b/data/challenges.json
index 51c1359..a8d0e34 100644
--- a/data/challenges.json
+++ b/data/challenges.json
@@ -14,7 +14,8 @@
"cost": 0,
"started_at": "2026-07-23T05:38:07.592Z",
"bytes": 2904,
- "finished_at": "2026-07-23T05:38:25.720Z"
+ "finished_at": "2026-07-23T05:38:25.720Z",
+ "thumb": true
},
{
"model": "qwen25-7b",
@@ -33,7 +34,8 @@
"cost": 0,
"started_at": "2026-07-23T05:36:16.579Z",
"bytes": 3117,
- "finished_at": "2026-07-23T05:36:44.762Z"
+ "finished_at": "2026-07-23T05:36:44.762Z",
+ "thumb": true
},
{
"model": "qwen3-14b",
@@ -43,7 +45,8 @@
"cost": 0,
"started_at": "2026-07-23T05:32:56.078Z",
"bytes": 1295,
- "finished_at": "2026-07-23T05:35:14.449Z"
+ "finished_at": "2026-07-23T05:35:14.449Z",
+ "thumb": true
},
{
"model": "kimi",
@@ -53,7 +56,8 @@
"cost": 0.0067,
"started_at": "2026-07-23T05:35:30.432Z",
"bytes": 1682,
- "finished_at": "2026-07-23T05:36:35.854Z"
+ "finished_at": "2026-07-23T05:36:35.854Z",
+ "thumb": true
}
],
"voted_at": "2026-07-23T05:51:00.009Z"
@@ -92,7 +96,8 @@
"cost": 0,
"started_at": "2026-07-23T05:32:19.764Z",
"bytes": 12191,
- "finished_at": "2026-07-23T05:32:56.077Z"
+ "finished_at": "2026-07-23T05:32:56.077Z",
+ "thumb": true
},
{
"model": "qwen25-7b",
@@ -119,7 +124,8 @@
"cost": 0.0162,
"started_at": "2026-07-23T05:29:23.128Z",
"bytes": 4559,
- "finished_at": "2026-07-23T05:32:28.001Z"
+ "finished_at": "2026-07-23T05:32:28.001Z",
+ "thumb": true
},
{
"model": "gpt",
@@ -138,7 +144,8 @@
"cost": 0.0443,
"started_at": "2026-07-23T05:29:23.131Z",
"bytes": 7691,
- "finished_at": "2026-07-23T05:29:58.821Z"
+ "finished_at": "2026-07-23T05:29:58.821Z",
+ "thumb": true
}
],
"voted_at": "2026-07-23T05:51:21.751Z"
@@ -167,7 +174,8 @@
"cost": 0,
"started_at": "2026-07-23T05:42:07.511Z",
"finished_at": "2026-07-23T05:42:37.973Z",
- "bytes": 3678
+ "bytes": 3678,
+ "thumb": true
},
{
"model": "hermes3-8b",
@@ -238,73 +246,82 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "error",
- "error": "never started — model host busy/down",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": "2026-07-23T06:23:45.482Z"
+ "status": "done",
+ "error": null,
+ "seconds": 29,
+ "cost": 0,
+ "started_at": "2026-07-23T06:24:16.536Z",
+ "finished_at": "2026-07-23T06:24:45.145Z",
+ "bytes": 1745
},
{
"model": "gemma3-12b",
- "status": "error",
- "error": "never started — model host busy/down",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": "2026-07-23T06:23:45.482Z"
+ "status": "done",
+ "error": null,
+ "seconds": 142,
+ "cost": 0,
+ "started_at": "2026-07-23T06:24:45.146Z",
+ "finished_at": "2026-07-23T06:27:06.672Z",
+ "bytes": 6543
},
{
"model": "hermes3-8b",
- "status": "error",
+ "status": "done",
"error": "never started — model host busy/down",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": "2026-07-23T06:23:45.482Z"
+ "seconds": 14,
+ "cost": 0,
+ "started_at": "2026-07-23T06:27:06.673Z",
+ "finished_at": "2026-07-23T06:27:20.328Z",
+ "bytes": 1644
},
{
"model": "qwen25-7b",
- "status": "error",
- "error": "never started — model host busy/down",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": "2026-07-23T06:23:45.482Z"
+ "status": "done",
+ "error": null,
+ "seconds": 13,
+ "cost": 0,
+ "started_at": "2026-07-23T06:24:53.435Z",
+ "finished_at": "2026-07-23T06:25:05.953Z",
+ "bytes": 2069
},
{
"model": "claude",
"status": "error",
- "error": "server restarted mid-run",
+ "error": "anthropic error: {\"type\":\"invalid_request_error\",\"message\":\"Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.\"}",
"seconds": 0,
- "cost": null
+ "cost": null,
+ "started_at": "2026-07-23T06:24:16.584Z",
+ "finished_at": "2026-07-23T06:24:16.964Z"
},
{
"model": "kimi",
- "status": "error",
- "error": "never started — model host busy/down",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": "2026-07-23T06:23:45.482Z"
+ "status": "done",
+ "error": null,
+ "seconds": 233,
+ "cost": 0.0253,
+ "started_at": "2026-07-23T06:24:16.608Z",
+ "finished_at": "2026-07-23T06:28:09.640Z",
+ "bytes": 17756
},
{
"model": "gpt",
- "status": "error",
- "error": "never started — model host busy/down",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": "2026-07-23T06:23:45.482Z"
+ "status": "done",
+ "error": null,
+ "seconds": 63,
+ "cost": 0.1075,
+ "started_at": "2026-07-23T06:24:16.619Z",
+ "finished_at": "2026-07-23T06:25:19.991Z",
+ "bytes": 23049
},
{
"model": "grok",
- "status": "error",
- "error": "never started — model host busy/down",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": "2026-07-23T06:23:45.482Z"
+ "status": "done",
+ "error": null,
+ "seconds": 137,
+ "cost": 0.1624,
+ "started_at": "2026-07-23T06:24:16.630Z",
+ "finished_at": "2026-07-23T06:26:33.966Z",
+ "bytes": 24146
}
]
},
@@ -318,12 +335,12 @@
{
"model": "qwen3-14b",
"status": "done",
- "error": null,
- "seconds": 70,
+ "error": "never started — model host busy/down",
+ "seconds": 75,
"cost": 0,
- "started_at": "2026-07-23T06:22:43.824Z",
- "finished_at": "2026-07-23T06:23:53.448Z",
- "bytes": 4800
+ "started_at": "2026-07-23T06:27:20.329Z",
+ "finished_at": "2026-07-23T06:28:35.081Z",
+ "bytes": 5679
},
{
"model": "gemma3-12b",
@@ -331,8 +348,8 @@
"error": "server restarted mid-run",
"seconds": 0,
"cost": null,
- "started_at": "2026-07-23T06:23:53.449Z",
- "finished_at": "2026-07-23T06:22:45.481Z"
+ "started_at": "2026-07-23T06:28:35.082Z",
+ "finished_at": "2026-07-23T06:28:29.518Z"
},
{
"model": "hermes3-8b",
@@ -341,23 +358,26 @@
"seconds": 0,
"cost": null,
"started_at": null,
- "finished_at": "2026-07-23T06:23:15.481Z"
+ "finished_at": "2026-07-23T06:28:29.518Z"
},
{
"model": "qwen25-7b",
- "status": "error",
+ "status": "done",
"error": "never started — model host busy/down",
- "seconds": 0,
- "cost": null,
- "started_at": null,
- "finished_at": "2026-07-23T06:23:15.481Z"
+ "seconds": 20,
+ "cost": 0,
+ "started_at": "2026-07-23T06:24:33.565Z",
+ "finished_at": "2026-07-23T06:24:53.433Z",
+ "bytes": 2810
},
{
"model": "claude",
"status": "error",
- "error": "server restarted mid-run",
+ "error": "anthropic error: {\"type\":\"invalid_request_error\",\"message\":\"Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.\"}",
"seconds": 0,
- "cost": null
+ "cost": null,
+ "started_at": "2026-07-23T06:24:16.965Z",
+ "finished_at": "2026-07-23T06:24:17.097Z"
},
{
"model": "kimi",
@@ -365,17 +385,18 @@
"error": "server restarted mid-run",
"seconds": 0,
"cost": null,
- "started_at": "2026-07-23T06:22:49.410Z",
- "finished_at": null
+ "started_at": "2026-07-23T06:28:09.641Z",
+ "finished_at": "2026-07-23T06:24:29.510Z"
},
{
"model": "gpt",
- "status": "error",
- "error": "server restarted mid-run",
- "seconds": 0,
- "cost": null,
- "started_at": "2026-07-23T06:22:51.342Z",
- "finished_at": null
+ "status": "done",
+ "error": "never started — model host busy/down",
+ "seconds": 44,
+ "cost": 0.0668,
+ "started_at": "2026-07-23T06:25:19.992Z",
+ "finished_at": "2026-07-23T06:26:04.183Z",
+ "bytes": 18310
},
{
"model": "grok",
@@ -383,8 +404,8 @@
"error": "server restarted mid-run",
"seconds": 0,
"cost": null,
- "started_at": "2026-07-23T06:22:52.378Z",
- "finished_at": null
+ "started_at": "2026-07-23T06:26:33.968Z",
+ "finished_at": "2026-07-23T06:24:29.510Z"
}
]
},
diff --git a/data/costlog.jsonl b/data/costlog.jsonl
index d3e4927..2a685d4 100644
--- a/data/costlog.jsonl
+++ b/data/costlog.jsonl
@@ -9,3 +9,7 @@
{"ts":"2026-07-23T06:11:03.114Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":91,"output_tokens":99,"cost_usd":0.001545}
{"ts":"2026-07-23T06:11:05.377Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":295,"output_tokens":108,"cost_usd":0.002505}
{"ts":"2026-07-23T06:12:00.481Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":92,"output_tokens":2223,"cost_usd":0.005613}
+{"ts":"2026-07-23T06:25:19.987Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":454,"output_tokens":7623,"cost_usd":0.107517}
+{"ts":"2026-07-23T06:26:04.181Z","provider":"openai","model":"gpt-5.1","task":"model-arena","input_tokens":558,"output_tokens":4703,"cost_usd":0.066819}
+{"ts":"2026-07-23T06:26:33.965Z","provider":"xai","model":"grok-4.5","task":"model-arena","input_tokens":650,"output_tokens":10696,"cost_usd":0.16239}
+{"ts":"2026-07-23T06:28:09.638Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena","input_tokens":459,"output_tokens":10016,"cost_usd":0.025315}
diff --git a/public/index.html b/public/index.html
index b49b794..43e1466 100644
--- a/public/index.html
+++ b/public/index.html
@@ -81,6 +81,17 @@ input[type=range]{accent-color:var(--neon);width:130px}
.pane .bar button{font-size:10px;padding:3px 10px}
.pane iframe{width:100%;height:460px;border:0;background:#000;display:block}
.pane .msg{height:460px;display:flex;align-items:center;justify-content:center;color:var(--dim);font-size:12px;padding:20px;text-align:center}
+.poster{position:relative;height:460px;cursor:pointer;background:#000;overflow:hidden}
+.poster img{width:100%;height:100%;object-fit:cover;object-position:top center;display:block;transition:transform .3s}
+.poster:hover img{transform:scale(1.03)}
+.poster .play{position:absolute;inset:auto 0 12px 0;margin:auto;width:max-content;background:rgba(7,8,15,.8);border:1px solid var(--neon);color:var(--neon);
+ padding:6px 16px;font-size:11px;letter-spacing:1px;text-transform:uppercase;border-radius:999px;opacity:0;transition:opacity .2s}
+.poster:hover .play{opacity:1}
+.pane.winner .poster{height:560px}
+/* thumbnails on the challenge cards */
+.card .shots{display:flex;gap:3px;margin:8px 0 2px;height:64px;border-radius:3px;overflow:hidden}
+.card .shots img{flex:1;height:64px;object-fit:cover;object-position:top center;min-width:0;background:#000}
+.card .shots .none{flex:1;display:flex;align-items:center;justify-content:center;background:var(--panel2);color:var(--dim);font-size:10px}
/* leaderboard */
table{width:100%;border-collapse:collapse;font-size:13px}
th,td{padding:10px 12px;border-bottom:1px solid var(--line);text-align:left}
@@ -197,8 +208,13 @@ async function loadList(){
const win = c.winner===r.model ? ' winner' : '';
return `<span class="chip ${r.status}${win}">${mLabel(r.model)}${c.winner===r.model?' 👑':''}${r.status==='error'?' ✗':''}</span>`;
}).join('');
+ const shots = c.runs.filter(r=>r.status==='done'&&r.thumb).slice(0,4);
+ const shotsHtml = shots.length
+ ? `<div class="shots">${shots.map(r=>`<img src="${API}/thumb/${c.id}/${r.model}" alt="" loading="lazy">`).join('')}</div>`
+ : '';
div.innerHTML = `<h3>${esc(c.title)}</h3>
<div class="when" title="${c.created_at}">🕓 ${fmtWhen(c.created_at)}</div>
+ ${shotsHtml}
<div class="chips">${chips}</div>`;
div.onclick = ()=>openDetail(c.id);
el.appendChild(div);
@@ -275,11 +291,17 @@ async function renderDetail(id, statusOnly){
stats.push(run.cost ? '$'+run.cost.toFixed(3) : '$0');
let bodyHtml;
if (run.status==='done')
- bodyHtml = `<iframe sandbox="allow-scripts" src="${API}/artifact/${c.id}/${run.model}" title="${mLabel(run.model)}"></iframe>`;
+ // thumbnail poster (if rendered) with a click-to-run-live overlay — avoids
+ // eagerly booting N live iframes at once, and gives the grid real imagery
+ bodyHtml = run.thumb
+ ? `<div class="poster" data-run="${c.id}/${run.model}" title="Click to run this artifact live">
+ <img src="${API}/thumb/${c.id}/${run.model}" alt="${mLabel(run.model)} artifact" loading="lazy">
+ <span class="play">▶ run live</span></div>`
+ : `<iframe sandbox="allow-scripts" src="${API}/artifact/${c.id}/${run.model}" title="${mLabel(run.model)}"></iframe>`;
else if (run.status==='error')
bodyHtml = `<div class="msg">✗ ${esc(run.error||'failed')}</div>`;
else
- bodyHtml = `<div class="msg">⏳ ${run.status}… local models render one at a time</div>`;
+ bodyHtml = `<div class="msg">⏳ ${run.status}… ${run.status==='running'?'generating':'waiting for a free host'}</div>`;
pane.innerHTML = `<div class="bar">
<span class="name">${mLabel(run.model)}</span>
<span class="stat">${run.status==='done'?stats.join(' · '):run.status}</span>
@@ -299,6 +321,10 @@ async function renderDetail(id, statusOnly){
await fetch(API+'/api/challenges/'+c.id+'/retry/'+e.target.dataset.retry,{method:'POST'});
openDetail(c.id);
});
+ arena.querySelectorAll('.poster').forEach(el=>el.onclick=()=>{
+ const [cid,mid]=el.dataset.run.split('/');
+ el.outerHTML = `<iframe sandbox="allow-scripts" src="${API}/artifact/${cid}/${mid}"></iframe>`;
+ });
return c;
}
diff --git a/server.js b/server.js
index 2314881..72c8f57 100644
--- a/server.js
+++ b/server.js
@@ -4,6 +4,17 @@
// metered models are opt-in per-run and cost-logged.
const http = require('http'), fs = require('fs'), path = require('path'), os = require('os');
const crypto = require('crypto');
+const { execFile } = require('child_process');
+
+// headless-Chrome path (macOS default; override with CHROME_BIN)
+const CHROME_BIN = process.env.CHROME_BIN || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
+// render an artifact HTML file to a PNG thumbnail (best-effort, never throws)
+function shootThumb(htmlPath, outPng, cb) {
+ if (!fs.existsSync(CHROME_BIN)) return cb && cb(new Error('no chrome'));
+ execFile(CHROME_BIN, ['--headless', '--disable-gpu', '--no-sandbox', '--hide-scrollbars',
+ '--window-size=800,600', '--virtual-time-budget=2800', '--screenshot=' + outPng,
+ 'file://' + htmlPath], { timeout: 20000 }, (err) => cb && cb(err || (fs.existsSync(outPng) ? null : new Error('no png'))));
+}
// load .env (gitignored; populated from secrets-manager) without a dependency
try {
@@ -62,6 +73,24 @@ saveChallenges(challenges);
function appendJsonl(file, obj) { try { fs.appendFileSync(file, JSON.stringify(obj) + '\n'); } catch {} }
+// one-time: backfill thumbnails for done artifacts that don't have one yet (throttled, sequential)
+function backfillThumbs() {
+ const jobs = [];
+ for (const c of challenges) for (const r of c.runs) {
+ if (r.status !== 'done') continue;
+ const dir = path.join(ART, c.id);
+ const html = path.join(dir, r.model + '.html'), png = path.join(dir, r.model + '.png');
+ if (fs.existsSync(html) && !fs.existsSync(png)) jobs.push({ c, r, html, png });
+ }
+ let i = 0;
+ (function next() {
+ if (i >= jobs.length) return;
+ const j = jobs[i++];
+ shootThumb(j.html, j.png, (e) => { if (!e) { j.r.thumb = true; saveChallenges(challenges); } next(); });
+ })();
+}
+setTimeout(backfillThumbs, 2000);
+
// ---------- generation ----------
const ARENA_PROMPT = (challenge) => `You are competing in a model arena against other AI models. Challenge:
@@ -185,9 +214,12 @@ function runModel(challenge, modelId) {
if (!html) throw new Error('no HTML document in model output (' + String(out.text || '').length + ' chars)');
const dir = path.join(ART, challenge.id);
fs.mkdirSync(dir, { recursive: true });
- fs.writeFileSync(path.join(dir, modelId + '.html'), html);
+ const htmlPath = path.join(dir, modelId + '.html');
+ fs.writeFileSync(htmlPath, html);
run.status = 'done'; run.seconds = Math.round((Date.now() - t0) / 1000);
run.cost = +(out.cost || 0).toFixed(4); run.bytes = Buffer.byteLength(html);
+ // best-effort thumbnail (async; UI picks it up on next poll)
+ shootThumb(htmlPath, path.join(dir, modelId + '.png'), (e) => { if (!e) { run.thumb = true; saveChallenges(challenges); } });
} catch (e) {
run.status = 'error'; run.error = String(e.message || e).slice(0, 300);
run.seconds = Math.round((Date.now() - t0) / 1000);
@@ -332,6 +364,13 @@ const server = http.createServer(async (req, res) => {
if (p === '/api/ledger') return send(res, 200, { models: buildLedger(), votes: challenges.filter(c => c.winner).length });
+ if ((m = p.match(/^\/thumb\/([a-f0-9]+)\/([\w-]+)$/))) {
+ const fp = path.join(ART, m[1], m[2] + '.png');
+ if (!fs.existsSync(fp)) { res.writeHead(404); return res.end('no thumb'); }
+ res.writeHead(200, { 'Content-Type': 'image/png', 'Cache-Control': 'no-cache' });
+ return res.end(fs.readFileSync(fp));
+ }
+
if ((m = p.match(/^\/artifact\/([a-f0-9]+)\/([\w-]+)$/))) {
const fp = path.join(ART, m[1], m[2] + '.html');
if (!fs.existsSync(fp)) { res.writeHead(404); return res.end('no artifact'); }
diff --git a/yolo/IDEAS.md b/yolo/IDEAS.md
new file mode 100644
index 0000000..0a583e6
--- /dev/null
+++ b/yolo/IDEAS.md
@@ -0,0 +1,16 @@
+# Model Arena — /yolo wild-ideas run (started 2026-07-22 ~22:45, ~2h window)
+
+Rails: $0 local by default · reversible Mac2 edits · gated actions → pending-approval · commit each win.
+Answering the /contrarian critique ("one builder vote = theater", "toy demos not real signal") IS the through-line.
+
+## Slate (DTD-ranked, highest-leverage first)
+1. **Artifact auto-screenshot** — headless-render each artifact → PNG thumbnail on the card + battle grid. Makes the grid visual, unblocks #2. [$0]
+2. **AI auto-referee (WILD centerpiece)** — Mac2 has 4 local VISION models (qwen2.5vl:7b, llava, moondream, minicpm-v). Screenshot each artifact, have a vision model score it (runs? matches prompt? visual quality 1-10?), auto-rank BEFORE the human vote. Kills "builder vote = theater". [$0 local vision]
+3. **ELO + cost-efficiency ledger** — pairwise ELO from battle outcomes; win-rate-per-dollar column. Real ranking, not just win%. [$0]
+4. **Blind mode** — hide model names during judging (Chatbot-Arena style) to strip brand bias; reveal after crown. [$0]
+5. **Auto daily-challenge generator** — a $0 all-local battle on a rotating prompt to accrue REAL ledger data over time (fixes "1 vote" theater). launchd install = GATED → draft memo. [$0 run, gated install]
+6. **Remix mode** — feed the winning artifact to another model: "improve this." Iterative arena. [$0 local]
+7. **Source diff view** — view each artifact's HTML source in-pane. [$0]
+
+## Log
+- [DONE] #1 Artifact auto-screenshot — thumbnails on cards (.shots strip) + battle panes (poster→click-to-run-live), startup backfill, /thumb route. Commit next.
← 84ddac1 mac1: revive Ollama (unstuck pinned qwen3:14b); restore aren
·
back to Model Arena
·
yolo idea 2: AI auto-referee — local vision model (qwen2.5vl dc3d732 →