Building a Dream Machine: The Tech Behind Collecting Dreams via WhatsApp
When a film director asks you to help collect dreams via WhatsApp, you say yes—then figure out the architecture later.
The Wild Idea
A few months ago, I got an unusual request. The filmmaker behind All Systems Collapse wanted to create something different: an immersive experience powered by the collective dreams of strangers.
The concept? People share their dreams via WhatsApp. An AI responds with a creative color—not an interpretation, not an analysis, just a beautiful artistic response that captures the essence of what they dreamed. These colors become part of a larger tapestry for an immersive film experience.
I said yes before I fully understood what I was getting into.
The Architecture
Here’s what we actually built:
Stack:
Node.js + Express backend
React + TypeScript frontend
PostgreSQL (Neon serverless) for persistence
Twilio WhatsApp Business API for messaging
OpenAI GPT-4 for dream processing
OpenAI Whisper for voice transcription
Drizzle ORM for type-safe database operations
The Flow:
User sends WhatsApp message (text/voice)
↓
Twilio webhook hits our Express server
↓
If voice → Whisper API transcribes to text
↓
GPT-4 classifies: greeting, question, or actual dream?
↓
If dream too short (<20 words) → ask clarifying questions
↓
Generate creative color response
↓
Send back via Twilio
↓
Log everything to PostgreSQL
Technical Challenges & Solutions
Challenge 1: Voice Messages in Two Languages
Users send voice notes at 6 AM, half-asleep, switching between Italian and English mid-sentence. OpenAI’s Whisper handles this beautifully—you just pass the audio buffer and it figures out the language..audio.transcriptions.create({
const transcription = await openai.audio.transcriptions.create({
file: audioFile,
model: “whisper-1”,
});
The trick was handling Twilio’s media URLs. They’re temporary and require authentication. We fetch the audio, buffer it, then pass it to Whisper.
Challenge 2: Smart Randomization Without Repetition
Nobody wants the same color twice in a row. We track the last 3 rewards per user and exclude them from the next generation:
const recentRewards = await storage.getRecentUserRewards(userId, 3);
const excludeColors = recentRewards.map(r => r.content);
// Pass to GPT-4: “Generate a color, but NOT any of these...”
Challenge 3: Short Dreams Need More Context
A message like “I flew” isn’t enough for a good color response. We built a state machine:
Dream received → check word count
< 20 words → status =
awaiting_details, generate clarifying questionsUser responds → combine content, check again
≥ 20 words → proceed to color generation
The clarifying questions are AI-generated based on the dream fragment—not generic. “You mentioned flying—were you soaring freely or struggling to stay up?”
Challenge 4: Message Classification
Not every WhatsApp message is a dream. “Hello!” is a greeting. “How does this work?” is a question. “I was swimming with elephants” is a dream.
We use GPT-4 with a classification prompt that returns structured JSON:
const classification = await classifyMessage(content);
// Returns: { shouldProcessAsDream: boolean, category: string, response?: string }
This lets us handle greetings warmly without wasting processing on non-dreams.
Challenge 5: Bilingual Everything
Italian and English throughout. Not just the bot’s responses—the admin dashboard, error messages, everything. We use a simple pattern: dual-language strings with / separator.
“Grazie per aver condiviso il tuo sogno! / Thank you for sharing your dream! ✨”Not the most elegant i18n, but perfect for a bilingual audience where many people understand both.
The Database Schema
Keeping it simple with Drizzle:
users: { id, phoneNumber, name, totalDreams, totalRewards }dreams: { id, userId, content, status, pendingRewardType }
rewards: { id, dreamId, userId, type, content }
messages: { id, userId, content, direction, category }
The status field on dreams is the state machine: received → analyzing → completed or awaiting_details → received → ...
Lessons for Technical Builders
1. Webhooks are the real API
Twilio’s webhook model means your server needs to be rock-solid. Handle every edge case. Return 200 OK even on errors (or Twilio retries).
2. AI classification saves everything
Before GPT-4 classification, we tried regex patterns for detecting greetings. Nightmare. Let the LLM do what it’s good at.
3. State machines > boolean flags
Instead of needsMoreDetails: boolean, use explicit statuses. Your future self will thank you.
4. Log everything
Every incoming message, every outgoing response, every state transition. When something weird happens at 3 AM (dreams don’t follow business hours), you need the trail.
5. Build the admin dashboard early
We can see dreams flowing in real-time, track user engagement, monitor the processing queue. Without this visibility, we’d be flying blind.
What’s Next?
As All Systems Collapse continues to gather dreams for its immersive experience, DreamBot keeps running—a quiet little machine, collecting fragments of the collective unconscious, one WhatsApp webhook at a time.
The code isn’t perfect. The architecture evolved through iteration. But it works, it scales, and every night, real people share real dreams with a machine that responds with something beautiful.
That’s the magic of building at the intersection of tech and art.
Your Turn
What’s the project that scares you technically? The integration you’ve never tried? The architecture that seems too ambitious?
Start it anyway. The dreams are waiting.
Tech stack: Node.js, Express, React, TypeScript, PostgreSQL, Twilio, OpenAI, Drizzle ORM. Total development time: too many late nights to count. Worth it? Absolutely. 🎨✨


