A modern photobooth application designed for events and parties. Capture, process, and print photos with real-time preview, cloud streaming support, and customizable print overlays.
- Multi-camera Support: Works with DSLR cameras (via gphoto2) and webcams
- Real-time Preview: Live camera feed with Socket.IO
- Image Processing: Automatic generation of thumbnails and display versions
- Photo Printing: DNP QW410 dye-sublimation printer support (CUPS on Linux/RPI)
- Print Frame Overlay: Add customizable PNG borders/frames to printed photos
- Cloud Streaming: Optional Azure integration for remote photo access
- Multi-interface: Controller, displayer, and manager views for flexible setups
- Zero Security Vulnerabilities: Modern dependencies with regular updates
Version 18.0.0 or higher is required.
node --version # Should output >= 18.0.0# Download and install Node.js 18.x
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# Verify installation
node --version
npm --version# Install gphoto2 runtime libraries (required)
sudo apt install libgphoto2-6 libgphoto2-port12
# Install development libraries (for legacy driver compilation if needed)
sudo apt install libgphoto2-dev
# Verify gphoto2 is working
gphoto2 --version
gphoto2 --auto-detect# Install CUPS for DNP QW410 printer
sudo apt install cups
# Add user to lpadmin group
sudo usermod -a -G lpadmin pi
# Configure printer via CUPS web interface
# http://localhost:631No additional system dependencies required. The application:
- Automatically uses your system webcam for capture
- Uses SIMULATION mode for printing (saves to
/public/print/folder instead of real printer)
git clone https://github.com/sescandell/halloothween.git
cd halloothween# Main project
npm install
# Azure streamer service (optional)
cd PhotoboothStreamer
npm install
cd ..The postinstall script will automatically attempt to install optional camera dependencies based on your platform.
Copy the example environment file and configure:
cp .env.example .envEdit .env with your settings:
# ============================================
# CAMERA CONFIGURATION
# ============================================
# Camera driver: auto, gphoto2, gphoto2-legacy, webcam
CAMERA_DRIVER=auto
# Capture mode
# - false: Direct capture (Raspberry Pi + gphoto2)
# - true: Pause stream before capture (Windows webcam)
PAUSE_STREAM_ON_CAPTURE=true
# ============================================
# PRINTER CONFIGURATION
# ============================================
# Enable/disable printer (true/false)
PRINTER_ENABLED=true
# Printer name (Linux/RPI only - Windows always uses SIMULATION)
# Options: "DNP_QW410" or "__SIMULATION__"
PRINTER_NAME=DNP_QW410
# Print mode
PRINTER_MODE=auto
# ============================================
# PRINT FRAME OVERLAY
# ============================================
# Enable/disable frame overlay on printed photos (true/false)
PRINT_FRAME_ENABLED=false
# Path to frame PNG overlay (relative to project root)
# Requirements: 1844x1240 pixels (10x15cm @ 300 DPI), PNG with transparency
PRINT_FRAME_PATH=
# ============================================
# AZURE STREAMING (OPTIONAL)
# ============================================
# Azure streamer URL
STREAMER_URL=http://192.168.0.56:3000
# Shared secret (same value as in your streamer)
STREAMER_SHARED_SECRET=
# Unique RPI identifier
RPI_ID=rpi-001
# Enable/disable streamer (true/false)
STREAMER_ENABLED=falseThe following directories will be automatically created on first run, but you can create them manually:
mkdir -p public/pictures public/thumbnails public/display public/print public/print-framednpm startThe server will start on port 8181.
Open your browser and navigate to:
- All-in-one interface: http://localhost:8181/all-in-one (controller + displayer)
- Controller: http://localhost:8181/controller (capture controls)
- Displayer: http://localhost:8181/displayer (photo display)
- Manager: http://localhost:8181/manager (photo management)
In a separate terminal:
cd PhotoboothStreamer
npm startThe streaming service will start on port 3000.
The application supports multiple camera drivers with automatic fallback:
| Driver | Description | Platform | Best For |
|---|---|---|---|
auto |
Automatic detection with fallback | All | Most users (recommended) |
gphoto2 |
Modern FFI-based driver | Linux/RPI | DSLR cameras (best performance) |
gphoto2-legacy |
Legacy native driver | Linux/RPI | Fallback if modern driver fails |
webcam |
System webcam driver | All | Windows dev or webcam setups |
Windows: Uses webcam driver automatically
Linux/Raspberry Pi: Tries gphoto2 → gphoto2-legacy → webcam (in order until one succeeds)
Set CAMERA_DRIVER in .env:
CAMERA_DRIVER=auto # Automatic detection (recommended)Or force a specific driver:
CAMERA_DRIVER=webcam # Force webcam driverThe application supports printing to DNP QW410 dye-sublimation printers via CUPS on Linux/Raspberry Pi. Windows automatically uses SIMULATION mode.
| Platform | Print Method | Output |
|---|---|---|
| Linux/RPI | CUPS (lp command) | Real printer (DNP_QW410) or simulation |
| Windows | SIMULATION | Saves to public/print/ folder |
- Format: 10x15 cm (4x6 inches)
- Resolution: 1844x1240 pixels @ 300 DPI
- Output: High-quality JPEG
- Install CUPS and configure DNP QW410 printer
- Verify printer is available:
lpstat -p -d
- Set
PRINTER_ENABLED=trueandPRINTER_NAME=DNP_QW410in.env
To test printing without a real printer:
# Linux/RPI - Use simulation
PRINTER_NAME=__SIMULATION__
# Windows - Always simulation (automatic)Photos will be copied to public/print/ folder.
Add custom PNG borders/frames to printed photos (e.g., event branding, text overlays).
- Customizable frames: Use any PNG overlay with transparency
- Automatic composition: Photo is resized and overlaid with frame before printing
- Graceful fallback: If frame is missing/invalid, prints without frame
- Persistent storage: Composed photos saved to
public/print-framed/for reference
- Dimensions: 1844x1240 pixels (exactly)
- Format: PNG with alpha channel (transparency)
- DPI: 300 (for 10x15 cm output)
- Structure:
- Top zone: Transparent (photo visible through)
- Bottom/sides: Opaque (frame decorations, text, logos)
- Create or obtain a PNG frame matching specifications above
- Save to
assets/print-frames/your-frame.png - Configure in
.env:PRINT_FRAME_ENABLED=true PRINT_FRAME_PATH=assets/print-frames/your-frame.png
An example frame template is provided at assets/print-frames/example-frame.png:
- Transparent top zone (1040px) for photo visibility
- Blue bottom zone (200px) with placeholder text
- Ready to use or customize in your image editor
See assets/print-frames/README.md for detailed instructions on creating custom frames.
PRINT_FRAME_ENABLED=false| Component | Technology | Version |
|---|---|---|
| Runtime | Node.js | >= 18.0.0 |
| Module System | ES Modules | Native |
| Web Framework | Express | 5.2.1 |
| Real-time | Socket.IO | 4.8.3 |
| Image Processing | Sharp | 0.34.5 |
| Template Engine | EJS | 4.0.1 |
| Camera (Modern) | @photobot/gphoto2-camera | 2.8.0 (optional) |
| Camera (Legacy) | gphoto2 | 0.3.2 (optional) |
| Camera (Webcam) | node-webcam | 0.8.1 (optional) |
halloothween/
├── server.js # Main server (Express 5 + Socket.IO)
├── routes.js # HTTP routes and Socket.IO handlers
├── config.js # Runtime application configuration
├── app-config.js # Application configuration factory
├── .env # Environment variables (create from .env.example)
│
├── utils/
│ ├── camera-config.js # Camera driver configuration
│ ├── CameraAdapter.js # Async factory for camera selection
│ ├── cameras/
│ │ ├── GPhoto2Camera.js # Modern gphoto2 driver (FFI-based)
│ │ ├── GPhoto2LegacyCamera.js # Legacy gphoto2 driver (native)
│ │ └── WebcamCamera.js # Webcam driver
│ ├── PrinterClient.js # Printer interface (CUPS/simulation)
│ ├── FrameComposer.js # Print frame overlay composition
│ ├── AzureStreamingClient.js # Azure connection client
│ ├── InMemoryStore.js # Simple in-memory storage
│ └── bmpToSharp.js # BMP format auto-detection
│
├── assets/
│ └── print-frames/ # PNG frame overlays for printing
│ ├── example-frame.png # Example template (1844x1240px)
│ └── README.md # Frame creation guide
│
├── public/
│ ├── pictures/ # Original photos (high quality)
│ ├── thumbnails/ # Thumbnail versions (158px wide)
│ ├── display/ # Display versions (1024px wide)
│ ├── print/ # Simulation mode prints (Windows)
│ ├── print-framed/ # Composed photos with frame overlay
│ ├── js/ # Client-side JavaScript
│ └── css/ # Stylesheets
│
├── views/ # EJS templates
│ ├── all-in-one.html
│ ├── controller.html
│ ├── displayer.html
│ └── manager.html
│
└── PhotoboothStreamer/ # Azure streaming service
├── server.js # Express 5 + Socket.IO server
└── package.json # Separate dependencies
When a photo is captured, Sharp generates multiple versions in parallel:
- Original (95% quality JPEG) →
public/pictures/ - Thumbnail (158px wide) →
public/thumbnails/ - Display (1024px wide) →
public/display/
When printing with frame overlay enabled:
- Composed (photo + frame, 1844x1240px) →
public/print-framed/ - Print (sent to CUPS or saved to
public/print/)
All processing is done asynchronously with Promise.all for maximum performance.
This project uses ES Modules (not CommonJS). All imports must use ES syntax:
// ✅ Correct (ES Modules)
import express from 'express';
import { Server } from 'socket.io';
export default myFunction;
export class MyClass {}
// ❌ Incorrect (CommonJS - not supported)
const express = require('express');
module.exports = myFunction;For development on Windows without a DSLR:
- Application automatically uses webcam
- Set
PAUSE_STREAM_ON_CAPTURE=truein.env - Printing uses SIMULATION mode (saves to
public/print/)
Windows webcams may return BMP format. The application automatically detects and converts BMP to JPEG using bmpToSharp.js.
To enable webcam preview over HTTP on Chrome:
- Navigate to:
chrome://flags/#unsafely-treat-insecure-origin-as-secure - Add your Raspberry Pi IP:
http://192.168.x.x:8181 - Restart Chrome
Check driver detection:
# Server logs show which driver is active:
[CameraAdapter] Using driver: webcam (Windows platform)
[CameraAdapter] Using driver: gphoto2 (modern driver loaded successfully)
[CameraAdapter] Using driver: gphoto2-legacy (fallback from modern driver)
Camera not detected (Linux/RPI):
# Verify gphoto2 can see your camera
gphoto2 --auto-detect
# Check camera is in PTP/MTP mode (not mass storage)Camera not detected (Windows):
# Check webcam permissions
Settings > Privacy > Camera > Allow desktop appsForce specific driver for testing:
# In .env
CAMERA_DRIVER=webcamPrinter not found:
# Check CUPS printer status (Linux/RPI)
lpstat -p -d
# Verify printer name matches .env
lpstat -p DNP_QW410Windows printing:
Windows always uses SIMULATION mode (by design). Check public/print/ for output files.
Test with simulation:
# Linux/RPI - Force simulation mode
PRINTER_NAME=__SIMULATION__Frame not detected:
- Check
PRINT_FRAME_ENABLED=truein.env - Verify
PRINT_FRAME_PATHpoints to valid PNG file - Check server logs for
[FRAME]messages
Frame quality issues:
- Ensure PNG is exactly 1844x1240 pixels
- Verify PNG has alpha channel (transparency)
- Check photo zone is transparent in image editor
- Test with
example-frame.pngfirst
Composed photos location:
# Check composed photos with frame
ls -lh public/print-framed/Error: "Cannot use import statement outside a module"
Make sure you're using Node.js >= 18.0.0 and package.json has "type": "module".
Error: "gphoto2 module not found" on Windows
This is normal. On Windows, the application automatically falls back to webcam mode.
Photos not generated:
Check file permissions:
ls -la public/pictures/ public/thumbnails/ public/display/Ensure directories exist and are writable.
- CHANGELOG.md: Version history and breaking changes
- CAMERA_SETUP.md: Camera configuration and troubleshooting
- PRINTER_IMPLEMENTATION.md: Printer setup and technical details
- QR_OPTIMIZATION.md: QR code generation optimization
- assets/print-frames/README.md: Frame creation guide
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit your changes:
git commit -m "Add my feature" - Push to the branch:
git push origin feature/my-feature - Open a Pull Request
See repository for license information.
sescandell
- GitHub: @sescandell
- Repository: halloothween