← back to Wallco Ai
drunk-animals: /api/drunk-animals/stats + 'tonight in numbers' bar on /live
4c0ff53bcbb034a4ce91046dedee894eb2212642 · 2026-05-13 01:32:18 -0700 · SteveStudio2
Steve wakes up to a single-glance readout above the design stream.
Stats bar shows:
- Total designs (live counter)
- Designs per hour (derived from earliest→latest timestamps)
- Average saturation across the collection
- Top-8 animal-tag pills (frog 23, orangutan 21, elephant 14, ...)
Endpoint reads from in-memory DESIGNS (filtered to category='drunk-
animals') and counts ANIMAL_TAGS occurrences in each design's motifs[]
array — only shows animals that llava actually identified, not just
prompted. Stats refresh every 60s on the live page.
Smoke-tested: 123 designs, 11 animal species, 16/hr, avg sat 0.273.
Files touched
Diff
commit 4c0ff53bcbb034a4ce91046dedee894eb2212642
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 01:32:18 2026 -0700
drunk-animals: /api/drunk-animals/stats + 'tonight in numbers' bar on /live
Steve wakes up to a single-glance readout above the design stream.
Stats bar shows:
- Total designs (live counter)
- Designs per hour (derived from earliest→latest timestamps)
- Average saturation across the collection
- Top-8 animal-tag pills (frog 23, orangutan 21, elephant 14, ...)
Endpoint reads from in-memory DESIGNS (filtered to category='drunk-
animals') and counts ANIMAL_TAGS occurrences in each design's motifs[]
array — only shows animals that llava actually identified, not just
prompted. Stats refresh every 60s on the live page.
Smoke-tested: 123 designs, 11 animal species, 16/hr, avg sat 0.273.
---
server.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/server.js b/server.js
index 8d01ce0..cdacd65 100644
--- a/server.js
+++ b/server.js
@@ -2214,6 +2214,42 @@ app.get('/api/drunk-animals/feed', (req, res) => {
res.json({ count: rows.length, total: DESIGNS.filter(d => d.category === 'drunk-animals').length, designs: rows });
});
+// ── /api/drunk-animals/stats — derived snapshot for the live viewer's
+// "tonight in numbers" bar. Animals are extracted from motifs[] (when
+// llava tagged them) so the bar reflects what actually rendered, not
+// just what was prompted.
+app.get('/api/drunk-animals/stats', (_req, res) => {
+ res.setHeader('Cache-Control', 'no-store');
+ const all = DESIGNS.filter(d => d.category === 'drunk-animals');
+ const total = all.length;
+ if (total === 0) return res.json({ total: 0 });
+ const ANIMAL_TAGS = ['frog','elephant','giraffe','orangutan','capybara','lemur','sloth','macaw','panda','peacock','parrot','monkey'];
+ const animalCounts = {};
+ let satSum = 0, satCount = 0, earliest = null, latest = null;
+ for (const d of all) {
+ const motifs = (d.motifs || []);
+ for (const a of ANIMAL_TAGS) if (motifs.includes(a)) animalCounts[a] = (animalCounts[a] || 0) + 1;
+ if (typeof d.saturation === 'number') { satSum += d.saturation; satCount++; }
+ const t = Date.parse(d.created_at || '');
+ if (t) {
+ if (!earliest || t < earliest) earliest = t;
+ if (!latest || t > latest) latest = t;
+ }
+ }
+ const animals = Object.entries(animalCounts).sort((a,b)=>b[1]-a[1]);
+ const elapsedMs = (earliest && latest) ? (latest - earliest) : 0;
+ const hoursElapsed = elapsedMs / 3_600_000;
+ const designsPerHour = hoursElapsed > 0.1 ? (total / hoursElapsed) : null;
+ res.json({
+ total,
+ animals, // [['frog',18],['orangutan',14],...]
+ avg_saturation: satCount ? +(satSum/satCount).toFixed(3) : null,
+ earliest_at: earliest ? new Date(earliest).toISOString() : null,
+ latest_at: latest ? new Date(latest).toISOString() : null,
+ designs_per_hour: designsPerHour ? +designsPerHour.toFixed(1) : null
+ });
+});
+
// ── /drunk-animals/live — auto-polling stream view of the overnight run.
// New designs slide in at the top every 30s. Dark immersive theme matches
// the collection's botanical-pop-Warhol aesthetic.
@@ -2236,6 +2272,14 @@ header .pulse::before { content:""; display:inline-block; width:7px; height:7px;
@keyframes pulse { 0%,100%{opacity:.4} 50%{opacity:1} }
header a { color:#e6d4f0; text-decoration:none; font-size:12px; padding:4px 10px; border:1px solid var(--line); border-radius:999px }
header a:hover { border-color:var(--pink); color:var(--pink) }
+.stats-bar { padding:14px 20px; background:rgba(255,255,255,.02); border-bottom:1px solid var(--line); display:flex; gap:16px; flex-wrap:wrap; align-items:center; font-size:12px }
+.stats-bar .stat { display:flex; flex-direction:column; gap:2px; padding:0 14px; border-right:1px solid var(--line) }
+.stats-bar .stat:last-child { border-right:0 }
+.stats-bar .stat-label { font-size:10px; letter-spacing:.18em; text-transform:uppercase; color:#9586a3 }
+.stats-bar .stat-value { font-family:'Playfair Display',serif; font-size:18px; color:#fff }
+.stats-bar .animals { display:flex; gap:6px; flex-wrap:wrap; padding-left:14px }
+.stats-bar .animal-pill { padding:3px 10px; border-radius:999px; background:rgba(255,121,198,.12); color:var(--pink); font-size:11px; white-space:nowrap }
+.stats-bar .animal-pill strong { color:#fff; margin-left:4px; font-weight:600 }
main { padding:20px; max-width:1500px; margin:0 auto }
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); gap:18px }
.card { background:#1a0f1a; border:1px solid var(--line); border-radius:14px; overflow:hidden; position:relative; opacity:0; transform:translateY(-12px); animation:slidein .5s ease forwards }
@@ -2260,6 +2304,12 @@ main { padding:20px; max-width:1500px; margin:0 auto }
<a href="/drunk-animals">Grid view</a>
<a href="/">wallco.ai</a>
</header>
+<div class="stats-bar" id="statsbar">
+ <div class="stat"><span class="stat-label">Total</span><span class="stat-value" id="s-total">—</span></div>
+ <div class="stat"><span class="stat-label">Per hour</span><span class="stat-value" id="s-rate">—</span></div>
+ <div class="stat"><span class="stat-label">Avg saturation</span><span class="stat-value" id="s-sat">—</span></div>
+ <div class="animals" id="s-animals"></div>
+</div>
<main>
<div class="grid" id="grid"></div>
<div class="empty" id="empty">Loading the menagerie…</div>
@@ -2317,8 +2367,24 @@ function updateNext() {
nextEl.textContent = 'next in ' + m + ':' + String(s).padStart(2,'0');
}
+async function pollStats() {
+ try {
+ const r = await fetch('/api/drunk-animals/stats');
+ const j = await r.json();
+ if (!j.total) return;
+ document.getElementById('s-total').textContent = j.total;
+ document.getElementById('s-rate').textContent = j.designs_per_hour != null ? j.designs_per_hour.toFixed(1) : '—';
+ document.getElementById('s-sat').textContent = j.avg_saturation != null ? j.avg_saturation.toFixed(2) : '—';
+ const ani = document.getElementById('s-animals');
+ ani.innerHTML = (j.animals || []).slice(0, 8)
+ .map(([name, n]) => '<span class="animal-pill">' + name + '<strong>' + n + '</strong></span>').join('');
+ } catch (e) { console.warn('stats poll failed', e); }
+}
+
poll();
+pollStats();
setInterval(poll, 30_000);
+setInterval(pollStats, 60_000);
setInterval(updateNext, 1000);
</script>
</body></html>`);
← 4dd3794 drunk-animals: bump dedup window 5 to 8
·
back to Wallco Ai
·
drunk-animals: 'Best of the night' rail on /live 768a775 →