A beautiful, interactive Year in Review template. Fork it, customize it, and deploy your own celebration of the year.
View Live Demo Β· Party code: demo2025
Once logged in, check out the Behind the Scenes page for a technical deep-dive!
- Interactive Timeline - Scroll through 12 months of events, milestones, and memories
- Real-time Presence - See who else is viewing with live cursors and online indicators
- Team Showcase - Celebrate your team members with customizable avatars
- Photo Clusters - Add photo memories throughout the timeline
- Guestbook - Let visitors leave messages
- Cookie Clicker - Leave cookies on major timeline events
- Emoji Reactions - Send floating emoji reactions across the screen
- Easter Eggs - Hidden surprises (try the Konami code...)
- Neobrutalist Design - Modern pastel palette with playful holiday touches
-
Fork & Clone
git clone https://github.com/YOUR_USERNAME/snowglobe.git cd snowglobe pnpm install -
Configure Your Content Edit the JSON files in
src/lib/config/:site.json- Title, subtitle, year, theme colorstimeline.json- Events, milestones, new hiresteam.json- Team members and avatarsmonth-blurbs.json- Monthly narrativesphotos.json- Photo cluster definitions
-
Add Your Avatars
- Place default avatars in
static/images/avatars/defaults/ - Place custom avatars in
static/images/avatars/custom/ - Recommended: 128x128px WebP, pixelated style
- Place default avatars in
-
Start Development
# Start the database pnpm db:start # Push database schema pnpm db:push # Seed users from config pnpm db:seed # Terminal 1: SvelteKit pnpm dev # Terminal 2: Gleam backend cd backend && gleam run
-
Deploy Deploy to your favorite platform (Fly.io, Vercel, Railway, etc.)
{
"site": {
"title": "Year in Review",
"subtitle": "A celebration of our journey",
"year": 2025,
"partyCode": "celebrate2025",
"footer": "Made with love"
},
"theme": {
"colors": {
"primary": "oklch(0.55 0.22 25)",
"secondary": "oklch(0.45 0.12 145)",
"accent": "oklch(0.75 0.14 85)"
}
},
"yearStats": [
{ "label": "New Hires", "value": "10", "emoji": "π₯" }
]
}Define your major events (one per month), minor events (sticky notes), and new hires:
{
"majorEvents": [
{
"id": "jan-kickoff",
"date": "2025-01-15",
"title": "New Year Kickoff",
"description": "Starting the year with fresh goals.",
"category": "milestone",
"emoji": "π",
"month": 0,
"stats": [{ "label": "Goals", "value": "12", "emoji": "π―" }]
}
],
"minorEvents": [
{ "id": "jan-note", "title": "Welcome!", "emoji": "β¨", "afterMonth": 0 }
],
"newHires": [
{ "id": "hire-1", "name": "Alex", "avatarId": "guest_1", "afterMonth": 0 }
]
}Define avatars and team members:
{
"avatars": {
"guest_1": {
"id": "guest_1",
"name": "Team Member",
"image": "defaults/avatar-red.webp",
"accentColor": "var(--holiday-red)"
}
},
"teamMembers": [
{
"id": "guest_1",
"name": "Alex Smith",
"title": "Engineer",
"image": "defaults/avatar-red.webp",
"accentColor": "var(--holiday-red)"
}
],
"guestAvatars": ["guest_1", "guest_2"],
"defaultPassword": "CHANGE_ME_2025"
}For the pixelated avatar style:
- Start with a photo (128x128px recommended)
- Apply pixelation effect (8-16px blocks)
- Export as WebP for best compression
- Place in
static/images/avatars/custom/
Browser <ββWSββ> Gleam (presence + writes) ββ> Postgres
β
βββ SSR ββ> SvelteKit (auth, reads)
| Layer | Responsibility | Port |
|---|---|---|
| SvelteKit | Auth (Lucia-style), SSR, read queries | 5173 (dev) / 3000 (prod) |
| Gleam | WebSocket server, cursor presence, guestbook writes | 4000 |
| Postgres | Users, sessions, guestbook entries, cookie likes | 5432 |
Frontend
- SvelteKit 2 + Svelte 5 (runes)
- Tailwind CSS 4
- shadcn-svelte components
- Valibot (config validation)
Backend
- Gleam on the BEAM
- Mist (WebSocket server)
- Pog (Postgres driver)
Database
- PostgreSQL
- Drizzle ORM (migrations & queries)
Auth
- Lucia-style sessions
- Argon2 password hashing
- SHA256 session tokens
Create a .env file:
DATABASE_URL=postgres://root:mysecretpassword@localhost:5432/local
PARTY_CODE=celebrate2025
PUBLIC_WS_URL=ws://localhost:4000/ws| Command | Description |
|---|---|
pnpm dev |
Start SvelteKit dev server |
pnpm build |
Build for production |
pnpm preview |
Preview production build |
pnpm check |
Type-check the codebase |
pnpm lint |
Lint with ESLint + Prettier |
pnpm format |
Format code with Prettier |
pnpm test |
Run tests with Vitest |
pnpm db:start |
Start Postgres via Docker |
pnpm db:push |
Push schema to database |
pnpm db:studio |
Open Drizzle Studio |
pnpm db:generate |
Generate migrations |
pnpm db:migrate |
Run migrations |
pnpm db:seed |
Seed database from config |
snowglobe/
βββ src/
β βββ lib/
β β βββ config/ # Configuration (edit these JSON files!)
β β β βββ site.json
β β β βββ timeline.json
β β β βββ team.json
β β β βββ month-blurbs.json
β β β βββ photos.json
β β β βββ schemas.ts # Valibot validation
β β β βββ loader.ts # Config loading
β β βββ components/ # Svelte components
β β βββ data/ # Data accessors
β β βββ realtime/ # WebSocket client & stores
β β βββ server/ # Auth & database utilities
β βββ routes/ # SvelteKit pages
βββ static/
β βββ images/
β βββ avatars/
β βββ defaults/ # Default geometric avatars
β βββ custom/ # Your custom avatars
βββ backend/ # Gleam WebSocket server
β βββ src/snowglobe/
βββ drizzle/ # Database migrations
βββ scripts/ # Utility scripts
The app can be deployed to Fly.io via GitHub Actions:
- Frontend: SvelteKit Node adapter β Fly.io (port 3000)
- Backend: Gleam β Fly.io (port 4000)
See .github/workflows/fly-deploy.yml for CI/CD configuration.
MIT - Fork it, customize it, make it yours!
Built with SvelteKit, Gleam, and lots of holiday cheer.
