Generate personalized X (Twitter) personality wrapped experiences with AI-powered analysis and cinematic video generation.
- SvelteKit - Web framework
- Svelte 5 - UI framework with runes
- TypeScript - Type safety
- TailwindCSS v4 - Styling
- Exa - Neural web search for public context alongside tweets (optional)
- OpenRouter - AI personality analysis (
openrouter/freemodel) - MagicHour - Video generation
- MongoDB - Persistent storage for wrapped jobs
- 📊 Twitter profile and tweet scraping
- 🧠 AI-powered personality archetype analysis
- 🎬 Cinematic video generation
- 📱 Responsive, mobile-first design
- 🔗 Social sharing integration
- ⬇️ Video download capability
- Clone and install dependencies
cd xwrapped
npm install- Configure environment variables
Copy .env.example to .env and add your API keys:
cp .env.example .envEdit .env with your values:
OPENROUTER_API_KEY=
EXA_API_KEY=
MAGIC_HOUR_API_KEY=
MONGODB_URI=
DEMO_MODE=
PUBLIC_BASE_URL=http://localhost:5173OPENROUTER_API_KEY— Required for personality analysis. Get a key at openrouter.ai/keys.MONGODB_URI— Required for storing jobs and results. Test withnpm run verify:mongo.EXA_API_KEY— Optional. When set, the pipeline runs Exa web search to enrich analysis with public web context. If missing, generation still runs without that step.
- OpenRouter: openrouter.ai/keys
- Exa: dashboard.exa.ai/api-keys
- MagicHour: magichour.ai/developer?tab=api-keys
- MongoDB: MongoDB Atlas (create a cluster, Database Access user, Network Access allow your IP, then copy the connection string and set the database name to
xwrappedin the URI path if needed)
npm run devOpen http://localhost:5173 in your browser.
npm run buildPreview the production build:
npm run previewFor demos or testing, pre-generate wrapped profiles for popular accounts:
npm run precacheThis runs the full pipeline for elonmusk, sama, naval, and others, storing results in MongoDB (MONGODB_URI must be set). Completed profiles are available at /profile/<handle>.
xwrapped/
├── src/
│ ├── lib/
│ │ ├── server/
│ │ │ ├── analyser.ts # OpenRouter personality analysis
│ │ │ ├── exa.ts # Optional Exa web search
│ │ │ ├── magichour.ts # MagicHour video generation
│ │ │ ├── db.ts # MongoDB-backed store
│ │ │ ├── mongo-connection.ts # MongoDB client
│ │ │ ├── pipeline.ts # Orchestrates full pipeline
│ │ │ └── types.ts # TypeScript types
│ │ └── components/ # UI components
│ ├── routes/
│ │ ├── +page.svelte # Landing page
│ │ ├── +layout.svelte # App layout
│ │ ├── layout.css # Tailwind + X dark theme
│ │ ├── api/
│ │ │ ├── generate/+server.ts # Start pipeline
│ │ │ └── status/[handle]/+server.ts # Poll status
│ │ ├── loading/[handle]/+page.svelte # Loading UI
│ │ └── profile/[handle]/ # Results page
├── scripts/
│ └── precache.ts # Pre-cache demo handles (requires MongoDB)
└── .env.example # Environment variables template
- User enters handle → Client validates and POSTs to
/api/generate - Data phase → Loads
{handle}_tweets_*.txtfrom the project root when present; otherwise stub profile + tweets (or import into MongoDB vianpm run import-tweets) - Analysis phase → OpenRouter analyzes personality and generates an archetype (optionally grounded with Exa when
EXA_API_KEYis set) - Video generation → MagicHour creates a cinematic video from the analysis
- Results → User is redirected to
/profile/<handle>for video, stats, share, and download
Jobs are keyed by normalized handle (lowercase, no @). The generate response id is that same handle string.
Starts the wrapped generation pipeline.
Request:
{
"handle": "elonmusk"
}Response:
{
"id": "elonmusk"
}The id matches the stored job key and URL segment (normalized handle).
Polls the status of a wrapped generation. Use the same handle as in the generate request (normalized).
Response:
{
"status": "complete",
"analysis": { },
"videoUrl": "https://...",
"handle": "elonmusk",
"profile": { },
"error": null
}When status is error, error contains a message string.
The app gracefully handles:
- Invalid Twitter handles
- Private or deleted accounts (when using live scraping)
- Insufficient tweet data
- API timeouts and failures
- Video generation errors
MIT