Files

9.4 KiB

YEG SpookyBooth 🎃

Edmonton Halloween Magic Photo Experience
An interactive, responsive, and kiosk-ready photo experience celebrating Canadian autumn folklore and Edmonton's river valley magic. Built with Next.js, React, TypeScript, and Tailwind CSS.

Users step into the enchanted portal, capture a photo, pick an iconic Canadian/Edmonton Halloween disguise, and receive a transformed 3D cartoon character portrait.


🌟 Key Features

  • 🍁 Edmonton & Canadian Halloween Atmosphere:
    • Eerie shimmering Northern Lights (Aurora Borealis).
    • Silhouettes of the iconic High Level Bridge, Walterdale Arch, glowing Muttart Conservatory Pyramids, and river valley spruce trees.
    • Radiant harvest moon, drifting autumn maple leaves, friendly bats, and a cute hovering ghost.
  • 🎨 Stylized 3D Cartoon Character Disguises:
    • 🌟 Vibe Matcher: Keep your exact face, outfit, hair & makeup completely unchanged, but replace the background with a funny, exaggerated scene that matches your vibe!
    • 🎸 Classic Mullet: Keep your photo portrait intact while rocking an iconic 80s mullet hairstyle.
    • 🏛️ Muttart Pyramid Witch: Whimsical young witch brewing emerald potions inside the glass conservatory pyramid.
    • 🦇 High Level Vampire: Chibi vampire in a winter velvet coat hovering above the foggy bridge.
  • 🧙‍♂️ 100% Magical Lore: Zero robotic AI technical jargon. The entire user experience is written as enchanting spooky spellcraft.
  • 🕹️ Tactile Cartoon Arcade UI:
    • Chunky 3D buttons (.btn-cartoon-orange, .btn-cartoon-purple, .btn-cartoon-dark) with satisfying downward click physics.
    • Bold comic-book outlines and playful cartoon wobbles.
  • 🧭 Complete Navigation:
    • ← Back buttons across all intermediate screens (Camera, Confirm, Style Selection, and Loading).
    • Dedicated Finish 🏁 button on the final result screen to reset the booth.
    • Top navigation bars use a strictly non-overlapping 3-column layout that never covers or blocks buttons.
  • 📲 QR Code & Print Ready:
    • On-screen QR code allows visitors to scan and instantly save their portrait to their smartphone.
    • Integrated 1-click photo printing formatted for 4x6 print media via @media print.
  • 🛡️ Intelligent Content Safety & Zero-Lockout Uptime:
    • Automatically identifies and handles image safety filter rejections (e.g. PROHIBITED_CONTENT, Safe-for-Work checks).
    • Safety rejections immediately return the user to the camera/photo capture screen with an interactive Spooky Safety Warning modal to snap or choose another photo.
    • No API cooldown lockouts: input rejections never penalize or disable the booth for subsequent guests.
  • ⏱️ Kiosk Inactivity Auto-Reset: Automatically resets to the standby welcome screen after N seconds of inactivity.
  • 🏷️ Sokaris Studio Attribution: Built-in footer attribution and copyright link to sokaris.com.

🖥️ Display Modes

1. Standard Mode (Default)

By default, the booth renders in a centered, vertical 9:16 kiosk viewport (max-width: 540px, max-height: 960px) on desktop and automatically expands on mobile screens. Ideal for testing, desktop browsing, and tablets:

http://localhost:3000

2. Fullscreen Kiosk Mode (?mode=fullscreen)

For physical event touchscreens, vertical digital signage, or dedicated kiosk hardware, add ?mode=fullscreen to the URL:

http://localhost:3000/?mode=fullscreen

What this does:

  1. Expands the viewport to 100% screen width and 100% screen height (100vw x 100vh).
  2. Removes all desktop borders, rounded corners, and margins.
  3. Automatically triggers native browser fullscreen (document.documentElement.requestFullscreen()) upon the user's first tap.

🚀 Quick Start

1. Prerequisites

  • Node.js 18.17+ or 20+
  • npm or yarn

2. Install Dependencies

cmd /c npm install

3. Configure Environment Variables

Copy .env.example to .env.local and add your API credentials:

# Google Gemini API Key (Primary Provider)
GEMINI_API_KEY=your_gemini_api_key_here

# OpenRouter API Key (Automatic Backup Provider)
OPENROUTER_API_KEY=your_openrouter_api_key_here

# Model Selection Override (Optional: "nanobanana 2", "nanobanana", "pro", "lite")
GENERATION_MODEL=nanobanana 2

(If no keys are provided, the app will run gracefully in demo fallback mode using pre-rendered sample portraits).

4. Run Development Server

cmd /c npm run dev

Open http://localhost:3000 (or http://localhost:3000/?mode=fullscreen) in your browser.

5. Build for Production

cmd /c npm run build
cmd /c npm start

For production deployment instructions on Linux with PM2, Nginx (karate.ph.sokaris.link), and user www-data, see deploy/PM2_PRODUCTION_SETUP.md.


⚙️ Configuration Guide

1. General Settings (config/booth.config.json)

All booth behaviors and visuals can be customized without editing code:

{
  "appName": "YEG SpookyBooth",
  "kioskMode": true,
  "theme": {
    "backgroundType": "color",
    "backgroundColor": "#0f091f",
    "primaryColor": "#ff6d00",
    "accentColor": "#22c55e",
    "surfaceColor": "#1e1136",
    "textColor": "#ffffff",
    "logoUrl": "/assets/brand/spooky_logo.svg",
    "logoWidth": 260
  },
  "camera": {
    "defaultFacingMode": "user",
    "aspectRatio": "9:16",
    "countdownSeconds": 3,
    "allowUpload": true,
    "maxUploadSizeMb": 10
  },
  "features": {
    "showQrCode": true,
    "showDownloadButton": true,
    "showPrintButton": true,
    "showRetakeButton": true
  },
  "limits": {
    "maxGenerationsPerDevice": 0,
    "cooldownMinutes": 60
  },
  "kiosk": {
    "enableAutoReset": true,
    "idleTimeoutSeconds": 90
  },
  "labels": {
    "welcomeTitle": "YEG SPOOKY BOOTH",
    "welcomeSubtitle": "Edmonton Halloween Magic Portal",
    "welcomeCta": "Tap To Enter If You Dare, Eh! 🎃",
    "cameraInstruction": "Strike your most spooktacular pose!",
    "cameraSnap": "Snap Spooky Photo",
    "cameraUpload": "Upload Photo",
    "confirmTitle": "Review Your Spooky Shot",
    "styleSelectionTitle": "Pick Your Halloween Disguise",
    "styleSelectionSubtitle": "Choose an iconic local legend to transform into",
    "processingTitle": "Brewing Your Transformation Spell...",
    "processingSubtitle": "Mixing river valley mist, northern starlight & potion sparks...",
    "resultTitle": "Your Spooktacular Portrait! 🎃",
    "scanQrLabel": "Scan to take your treat home",
    "downloadLabel": "Save Portrait",
    "printLabel": "Print Photo",
    "startOverLabel": "Haunt Again"
  }
}

2. Styles & Prompts (config/styles.json)

To add, remove, or modify character disguise presets, edit config/styles.json:

[
  {
    "id": "yeg_hockey_phantom",
    "name": "YEG Hockey Phantom",
    "description": "Friendly vintage hockey ghost on river ice under the glowing Northern Lights",
    "thumbnail": "/assets/styles/hockey_ghost.jpg",
    "prompt": "Vibrant 3D cartoon animated character portrait based on the person in the user's photo... Bubbly cartoon animation style, Hotel Transylvania and Pixar aesthetics...",
    "negativePrompt": "scary, bloody, gore, grotesque, deformed hands, bad anatomy, realistic human skin pores, text watermark"
  }
]

📁 Project Structure

├── app/
│   ├── api/
│   │   ├── config/route.ts      # API endpoint serving booth config & styles
│   │   └── generate/route.ts    # AI image transformation endpoint with failover
│   ├── globals.css              # Design tokens, cartoon button physics & animations
│   ├── layout.tsx               # Root HTML layout & meta tags
│   └── page.tsx                 # Main state machine routing screens
├── components/
│   ├── KioskFrame.tsx           # Kiosk frame, ?mode=fullscreen handler & footer
│   ├── SpookyAtmosphere.tsx     # Animated Edmonton skyline, aurora & falling leaves
│   └── screens/
│       ├── StandbyScreen.tsx    # Welcome screen with bouncy hero pumpkin
│       ├── CameraScreen.tsx     # Camera capture, countdown, and face guide
│       ├── ConfirmScreen.tsx    # Photo review with retake / confirm
│       ├── StyleScreen.tsx      # 2x2 grid of cartoon character cards
│       ├── LoadingScreen.tsx    # Cauldron animation with magical brewing steps
│       ├── ResultScreen.tsx     # Confetti celebration, download, QR code & Finish
│       └── RateLimitedScreen.tsx# Rate-limit cooldown screen
├── config/
│   ├── booth.config.json        # Main configuration file
│   └── styles.json              # Character styles and prompt presets
├── lib/
│   ├── ai/                      # Gemini & OpenRouter provider orchestration
│   ├── config.ts                # Server-side configuration loader
│   └── ratelimit.ts             # Device generation limits & cooldown tracker
└── public/
    └── assets/
        ├── brand/               # Spooky logo SVG & Sokaris Studio logos
        └── styles/              # Cartoon character artwork images

🏷️ Credits & License

  • Designed and developed for YEG Halloween Photo Experiences.
  • Attribution & branding by Sokaris Studio (sokaris.com).