← back to Wallco Ai
drunk-animal overnight generator + /admin/reload-designs
66cfe473cc1970ff7d68be95b694cabd028b21f1 · 2026-05-12 17:51:46 -0700 · SteveStudio2
Steve asked for 20 drunk-animals-in-forest, botanical-pop-Warhol
seamless tile designs + 1 new design every 4 min until 6am PT.
- scripts/drunk_animal_prompts.js — 15 animals × 15 tableaux × 10
botanicals × 10 Warhol palettes × 10 styles × 10 compositions
prompt library (frogs, elephants, giraffes, orangutans + wildcards)
- scripts/drunk_animals_batch.js — fires N (default 20) one-shot
through generate_designs.js (replicate backend)
- scripts/drunk_animals_tick.js — single-design tick; auto-bails
outside the 18:00→06:00 PT window so launchd can stay loaded
- ~/Library/LaunchAgents/com.steve.wallco-drunk-animals.plist
(StartInterval 240s) fires the tick
- server.js — POST /admin/reload-designs (localhost-admin) so the
tick can refresh designs.json + re-load in-memory grid without
pm2 restart. Smoke-tested 228 → 1469 designs.
Initial batch landed: 20/20 OK, IDs 1520-1539. Viewer search 'drunk'
returns 20 results immediately.
Files touched
A scripts/drunk_animal_prompts.jsA scripts/drunk_animals_batch.jsA scripts/drunk_animals_tick.js
Diff
commit 66cfe473cc1970ff7d68be95b694cabd028b21f1
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue May 12 17:51:46 2026 -0700
drunk-animal overnight generator + /admin/reload-designs
Steve asked for 20 drunk-animals-in-forest, botanical-pop-Warhol
seamless tile designs + 1 new design every 4 min until 6am PT.
- scripts/drunk_animal_prompts.js — 15 animals × 15 tableaux × 10
botanicals × 10 Warhol palettes × 10 styles × 10 compositions
prompt library (frogs, elephants, giraffes, orangutans + wildcards)
- scripts/drunk_animals_batch.js — fires N (default 20) one-shot
through generate_designs.js (replicate backend)
- scripts/drunk_animals_tick.js — single-design tick; auto-bails
outside the 18:00→06:00 PT window so launchd can stay loaded
- ~/Library/LaunchAgents/com.steve.wallco-drunk-animals.plist
(StartInterval 240s) fires the tick
- server.js — POST /admin/reload-designs (localhost-admin) so the
tick can refresh designs.json + re-load in-memory grid without
pm2 restart. Smoke-tested 228 → 1469 designs.
Initial batch landed: 20/20 OK, IDs 1520-1539. Viewer search 'drunk'
returns 20 results immediately.
---
scripts/drunk_animal_prompts.js | 171 ++++++++++++++++++++++++++++++++++++++++
scripts/drunk_animals_batch.js | 47 +++++++++++
scripts/drunk_animals_tick.js | 49 ++++++++++++
3 files changed, 267 insertions(+)
diff --git a/scripts/drunk_animal_prompts.js b/scripts/drunk_animal_prompts.js
new file mode 100644
index 0000000..daa909a
--- /dev/null
+++ b/scripts/drunk_animal_prompts.js
@@ -0,0 +1,171 @@
+#!/usr/bin/env node
+/**
+ * Drunk-animals-in-a-forest, botanical-pop-Warhol prompt library + picker.
+ *
+ * Generates highly varied prompts for Steve's overnight wallco.ai run.
+ * Each prompt combines:
+ * - one animal (frog/elephant/giraffe/orangutan + a few wildcards)
+ * - one "drunk" tableau (passed out, holding martini, swaying, etc.)
+ * - one botanical-forest context (banana leaves, ferns, jungle vines, etc.)
+ * - one Warhol-pop palette (electric pink + lime, cobalt + neon orange, etc.)
+ * - one composition cue (silkscreen 4-panel color swap, half-drop repeat, etc.)
+ *
+ * Used by:
+ * - bin/drunk_animals_batch.js — initial batch of N
+ * - bin/drunk_animals_tick.js — single design per 4-min cron firing
+ */
+
+const ANIMALS = [
+ 'wide-eyed Amazonian tree frog',
+ 'pink-cheeked drunken bullfrog',
+ 'tipsy emerald glass frog',
+ 'jolly grey African elephant calf',
+ 'wobbly pink champagne elephant',
+ 'reticulated giraffe with crooked smile',
+ 'long-lashed Maasai giraffe sipping a daiquiri',
+ 'shaggy orange orangutan clutching a banana cocktail',
+ 'sleepy Bornean orangutan slumped against a fern',
+ 'sloshed scarlet macaw',
+ 'tipsy capybara holding a mint julep',
+ 'drunken peacock with feathers half-fanned',
+ 'plastered red panda',
+ 'inebriated lemur swinging from a vine',
+ 'starry-eyed sloth holding a wine glass',
+];
+
+const TABLEAUX = [
+ 'passed out belly-up in a clearing of giant monstera leaves',
+ 'mid-dance, martini glass sloshing, eyes crossed',
+ 'cheeks flushed cartoon-red, leaning on a giant rhubarb stem',
+ 'spinning a tiny umbrella in a coconut cocktail, banana leaves overhead',
+ 'staggering through fiddlehead ferns with a hiccup bubble',
+ 'hugging a giant philodendron leaf, content smile',
+ 'tongue out, surrounded by floating champagne bubbles',
+ 'singing into a hibiscus flower microphone',
+ 'tangled in a hanging fern, drink balanced on its head',
+ 'reclining on a king-protea bloom, sipping nectar',
+ 'mid-yawn, glass tilted, dripping starlight',
+ 'doing a wobbly waltz with a vine partner',
+ 'one eye open one eye closed, holding two glasses',
+ 'jumping into a puddle of melted neon',
+ 'face-planting into a moss bed of orchids',
+];
+
+const BOTANICALS = [
+ 'oversized monstera, banana leaves, calathea, peace lilies',
+ 'tropical ferns, bird-of-paradise blossoms, philodendron, jungle vines',
+ 'rainforest canopy, bromeliads, palmettos, dripping pothos',
+ 'amazon understory, heliconia, anthuriums, climbing wisteria',
+ 'misty jungle floor, hostas, elephant-ear leaves, bursts of plumeria',
+ 'overgrown botanical garden, fiddleheads, dahlias, mossy logs',
+ 'lush midnight forest, glowing fungi, ferns, hanging mosses',
+ 'enchanted glade, oversized poppies, peonies, lotus pads',
+ 'verdant arboretum, monstera deliciosa, hibiscus, traveler palms',
+ 'wild herbarium, parsley, mint, rosemary, oversized blossoms',
+];
+
+const PALETTES = [
+ 'Warhol electric pink + acid lime + cobalt blue + lemon yellow',
+ 'silkscreen hot magenta + traffic-cone orange + emerald green + black',
+ 'neon coral + spearmint + buttercream + jet black ink',
+ 'shocking purple + sour apple green + Pepto pink + chrome',
+ 'Day-Glo orange + ultramarine + chartreuse + ivory',
+ 'Marilyn-era turquoise + persimmon + canary + slate',
+ 'Velvet Underground crimson + neon green + cream + onyx',
+ 'art-pop tangerine + grape + lime sherbet + white',
+ 'Brillo-box red, blue, yellow primaries on bone-white',
+ 'campbell-soup tomato red + sunshine yellow + grass green + matte black',
+];
+
+const STYLES = [
+ 'classic Andy Warhol silkscreen, 4-panel color-swap repeat, registration offset, halftone grain',
+ 'hand-pulled screen print, ink bleed at every edge, paper-grain texture, slight misregister',
+ 'bold flat-color pop-art, thick black outline, comic-book half-tones',
+ 'risograph-style 3-color overlap, slight pixel grain, hand-printed feel',
+ 'mid-century botanical lithograph crossed with Warhol pop, archival paper',
+ 'silkscreen poster from a 1968 nightclub, faded ink, off-register layers',
+ 'editorial pop-art collage, cut-paper feel, hand-stenciled forms',
+ '1980s Memphis-era illustration: bold geometry, pop colors, botanical mash-up',
+ 'modern silkscreen wallpaper repeat, painterly brushwork, deliberate ink drips',
+ 'cartoon-pop illustration in the spirit of a vintage Warhol Velvet Underground sleeve',
+];
+
+const COMPOSITIONS = [
+ 'seamless half-drop repeat, animals tessellated',
+ 'continuous scatter, motifs floating among leaves',
+ 'striped column repeat, animals in vertical bands',
+ 'mirrored axis repeat, kaleidoscopic motif',
+ 'large-scale focal repeat, one hero animal centered then scattered',
+ 'tossed-bouquet repeat, animals interspersed with botanicals',
+ 'tight 12-inch tile repeat, dense scatter',
+ 'oversized 36-inch mural repeat with breathing space',
+ 'allover repeat with diagonal flow, vines guiding the eye',
+ 'grid-of-portraits Warhol layout, alternating color cells',
+];
+
+function pickRand(arr, exclude = new Set()) {
+ for (let tries = 0; tries < 50; tries++) {
+ const v = arr[Math.floor(Math.random() * arr.length)];
+ if (!exclude.has(v)) return v;
+ }
+ return arr[Math.floor(Math.random() * arr.length)];
+}
+
+function buildPrompt(seed = null) {
+ if (seed != null) {
+ // Deterministic when given a seed — for reproducibility from a 4-min cron
+ const rng = (() => {
+ let s = seed >>> 0;
+ return () => { s = (s * 1664525 + 1013904223) >>> 0; return s / 2**32; };
+ })();
+ const pick = arr => arr[Math.floor(rng() * arr.length)];
+ return [
+ pick(ANIMALS), pick(TABLEAUX),
+ `set among ${pick(BOTANICALS)}`,
+ `palette: ${pick(PALETTES)}`,
+ pick(STYLES),
+ pick(COMPOSITIONS),
+ 'seamless wallpaper repeat, archival quality, no text or watermark'
+ ].join(', ');
+ }
+ return [
+ pickRand(ANIMALS), pickRand(TABLEAUX),
+ `set among ${pickRand(BOTANICALS)}`,
+ `palette: ${pickRand(PALETTES)}`,
+ pickRand(STYLES),
+ pickRand(COMPOSITIONS),
+ 'seamless wallpaper repeat, archival quality, no text or watermark'
+ ].join(', ');
+}
+
+function buildN(n) {
+ // Try hard to avoid repeats across the batch — track used animals/styles
+ const usedAnimals = new Set();
+ const usedStyles = new Set();
+ const out = [];
+ for (let i = 0; i < n; i++) {
+ if (usedAnimals.size >= ANIMALS.length - 1) usedAnimals.clear();
+ if (usedStyles.size >= STYLES.length - 1) usedStyles.clear();
+ const animal = pickRand(ANIMALS, usedAnimals); usedAnimals.add(animal);
+ const style = pickRand(STYLES, usedStyles); usedStyles.add(style);
+ const parts = [
+ animal,
+ pickRand(TABLEAUX),
+ `set among ${pickRand(BOTANICALS)}`,
+ `palette: ${pickRand(PALETTES)}`,
+ style,
+ pickRand(COMPOSITIONS),
+ 'seamless wallpaper repeat, archival quality, no text or watermark'
+ ];
+ out.push(parts.join(', '));
+ }
+ return out;
+}
+
+module.exports = { buildPrompt, buildN, ANIMALS, TABLEAUX, BOTANICALS, PALETTES, STYLES, COMPOSITIONS };
+
+if (require.main === module) {
+ const n = parseInt(process.argv[2] || '5', 10);
+ const prompts = buildN(n);
+ for (const p of prompts) console.log(p + '\n');
+}
diff --git a/scripts/drunk_animals_batch.js b/scripts/drunk_animals_batch.js
new file mode 100644
index 0000000..97340fe
--- /dev/null
+++ b/scripts/drunk_animals_batch.js
@@ -0,0 +1,47 @@
+#!/usr/bin/env node
+/**
+ * Fire a batch of N drunk-animal-in-forest, botanical-pop-Warhol designs
+ * through scripts/generate_designs.js. Each design uses a unique prompt
+ * from drunk_animal_prompts.buildN().
+ *
+ * Usage: node scripts/drunk_animals_batch.js [N] (default 20)
+ *
+ * Each design is pipelined separately so a single Replicate failure
+ * doesn't kill the whole batch. The generator writes to
+ * dw_unified.spoon_all_designs with kind='seamless_tile', category='drunk-animals'.
+ */
+const { spawnSync } = require('child_process');
+const path = require('path');
+const { buildN } = require('./drunk_animal_prompts');
+
+const N = parseInt(process.argv[2] || '20', 10);
+const ROOT = path.join(__dirname, '..');
+
+console.log(`\n=== Drunk-animal batch: firing ${N} designs ===\n`);
+
+const prompts = buildN(N);
+let ok = 0, fail = 0;
+
+for (let i = 0; i < prompts.length; i++) {
+ const p = prompts[i];
+ console.log(`\n--- [${i+1}/${N}] ${p.slice(0, 90)}…`);
+ const r = spawnSync('node', [
+ path.join(ROOT, 'scripts', 'generate_designs.js'),
+ '--n', '1',
+ '--kind', 'seamless_tile',
+ '--category', 'drunk-animals',
+ '--prompts', p,
+ ], { stdio: 'inherit', cwd: ROOT });
+ if (r.status === 0) ok++; else fail++;
+}
+
+console.log(`\n=== Batch done: ${ok} ok, ${fail} failed ===`);
+
+// Refresh designs.json from PG and push live server to reload its in-memory
+// grid so the new batch shows on wallco.ai immediately.
+spawnSync('python3', [path.join(ROOT, 'scripts', 'refresh_designs_snapshot.py')],
+ { stdio: 'inherit', cwd: ROOT, timeout: 120_000 });
+spawnSync('curl', ['-sf', '-m', '5', '-X', 'POST', 'http://127.0.0.1:9792/admin/reload-designs'],
+ { stdio: 'inherit', cwd: ROOT, timeout: 10_000 });
+
+process.exit(fail === 0 ? 0 : 1);
diff --git a/scripts/drunk_animals_tick.js b/scripts/drunk_animals_tick.js
new file mode 100644
index 0000000..6feba15
--- /dev/null
+++ b/scripts/drunk_animals_tick.js
@@ -0,0 +1,49 @@
+#!/usr/bin/env node
+/**
+ * Single-tick drunk-animal design generator — meant to be fired every 4 min
+ * by ~/Library/LaunchAgents/com.steve.wallco-drunk-animals.plist.
+ *
+ * Stops itself at STOP_HOUR (default 6 PT) by exiting 0 without generating
+ * anything — so launchd silently no-ops past the cutoff.
+ *
+ * Usage: node scripts/drunk_animals_tick.js
+ */
+const { spawnSync } = require('child_process');
+const path = require('path');
+const { buildPrompt } = require('./drunk_animal_prompts');
+
+const STOP_HOUR = parseInt(process.env.STOP_HOUR || '6', 10); // 6 AM
+const STOP_MIN = parseInt(process.env.STOP_MIN || '0', 10);
+
+// Bail past the cutoff. We use America/Los_Angeles regardless of system TZ.
+const nowPT = new Date(new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
+const h = nowPT.getHours(), m = nowPT.getMinutes();
+const afterCutoff = (h > STOP_HOUR) || (h === STOP_HOUR && m >= STOP_MIN);
+// Only the (likely 18:00→23:59 + 00:00→06:00) overnight window should fire.
+// Hours 06:00-17:59 are outside the run.
+const inRunWindow = (h >= 18) || (h < STOP_HOUR) || (h === STOP_HOUR && m < STOP_MIN);
+if (!inRunWindow) {
+ console.log(`[tick] skip — outside run window (PT ${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}, cutoff ${STOP_HOUR}:${STOP_MIN.toString().padStart(2,'0')})`);
+ process.exit(0);
+}
+
+const ROOT = path.join(__dirname, '..');
+const prompt = buildPrompt();
+console.log(`[tick ${new Date().toISOString()}] ${prompt.slice(0, 100)}…`);
+
+const r = spawnSync('node', [
+ path.join(ROOT, 'scripts', 'generate_designs.js'),
+ '--n', '1',
+ '--kind', 'seamless_tile',
+ '--category', 'drunk-animals',
+ '--prompts', prompt,
+], { stdio: 'inherit', cwd: ROOT, timeout: 4 * 60_000 });
+
+// Refresh designs.json snapshot from PG, then ping reload endpoint so the
+// live server picks up the new row in its in-memory grid.
+spawnSync('python3', [path.join(ROOT, 'scripts', 'refresh_designs_snapshot.py')],
+ { stdio: 'inherit', cwd: ROOT, timeout: 60_000 });
+spawnSync('curl', ['-sf', '-m', '5', '-X', 'POST', 'http://127.0.0.1:9792/admin/reload-designs'],
+ { stdio: 'inherit', cwd: ROOT, timeout: 10_000 });
+
+process.exit(r.status || 0);
← f78cfec wallco-ai: log Gemini usage to cost-tracker at 2 call sites
·
back to Wallco Ai
·
by-color: 5min in-memory TTL cache (FIFO 1k cap, sorted-excl 97ebc80 →