Inspiration

The idea for VLC Spectre came from a simple question: "What if your media player was haunted?" I wanted to create something that subverted the familiar, comfortable experience of watching videos by introducing subtle corruption and unexpected horror elements. VLC Media Player, being open-source and highly customizable, was the perfect canvas for this experiment.

The concept of "corrupted software" as a horror medium has always fascinated me - think creepypasta like "Ben Drowned" or games like "Doki Doki Literature Club." I wanted to bring that unsettling feeling to an everyday application that people trust and use regularly.

What It Does

VLC Spectre is a custom-built version of VLC Media Player 3.0.21 compiled from source with integrated horror modifications:

Custom "Spectre" Skin

  • Dark horror-themed interface (800x600 window)
  • Custom button graphics with blood splatter effects
  • Intentional UI corruption elements:
    • Play button offset by +5px (subtle misalignment)
    • Volume slider 3px narrower than its background (visual glitch)
    • Hidden full-screen overlay panel for jumpscares

Horror Extension (Lua)

  • Automatically activates on VLC startup
  • Checks every 30 seconds during playback
  • 5% chance per check to trigger a scare event
  • Downloads scary images from Unsplash API
  • Downloads scary sounds from Freesound API
  • Displays images via VLC's OSD and logo filter
  • Plays sounds through the playlist
  • Logs "HELP ME" to the console when triggered
  • Caches downloaded content locally

The result is a fully functional media player that works perfectly... until it doesn't. The subtle UI corruption creates an uneasy feeling, and the random horror events ensure you're never quite comfortable watching videos.

How I Built It

Technologies Used

  • VLC Media Player 3.0.21 - Compiled from source with custom modifications
  • MSYS2/MinGW-w64 - Windows build environment for compiling VLC
  • VLC Skins2 XML - Custom UI framework for the corrupted interface
  • Lua 5.1 - Extension scripting language (VLC's embedded version)
  • Unsplash API - Source for scary images
  • Freesound API - Source for scary sounds
  • Python + lxml - Property-based testing framework
  • PowerShell - Launcher scripts and automation

Development Process

The entire project was built using Kiro AI as my primary development partner, leveraging its spec-driven development feature extensively.

Phase 1: Requirements Definition I started by creating a detailed requirements document that defined:

  • 7 major requirements with 25+ acceptance criteria
  • Specific UI corruption elements (button offset, slider width mismatch)
  • VLC Skins2 XML structure requirements
  • Lua extension behavior specifications

Phase 2: Design Architecture Using Kiro, I developed a comprehensive design document that included:

  • XML component structure and hierarchy
  • Coordinate system and asset specifications
  • 7 correctness properties for validation
  • Error handling strategies
  • Property-based testing approach

Phase 3: Task Breakdown Kiro helped me break down the implementation into 9 major tasks with 30+ subtasks:

  • Theme XML structure creation
  • Bitmap resource declarations
  • Button implementations (6 media controls)
  • Slider controls (time and volume)
  • Hidden panel for jumpscares
  • Property-based test suite (7 properties)
  • Unit tests for specific elements

Phase 4: Implementation I worked through each task systematically, with Kiro generating:

  • Complete VLC Skins2 XML theme file
  • Lua extension code with API integration
  • Auto-loader interface for extension activation
  • Launcher scripts (Batch and PowerShell)
  • Comprehensive documentation (6 markdown files)

Phase 5: Integration The final step was integrating everything into the VLC source tree:

  • Copied assets to vlc-3.0.21/share/skins2/default/
  • Placed Lua scripts in vlc-3.0.21/share/lua/
  • Created build scripts for MSYS2 compilation
  • Developed launcher scripts for easy testing

What I Learned

Technical Skills

VLC Internals: I gained deep knowledge of VLC's architecture:

  • Skins2 XML DTD specification and element hierarchy
  • Lua API for extensions (vlc.stream, vlc.osd, vlc.playlist)
  • VLC's command-line interface and configuration system
  • Build process for compiling VLC from source on Windows

API Integration: Implemented real-world API calls in Lua:

  • HTTP requests using vlc.stream()
  • JSON parsing (manual, since Lua 5.1 lacks native support)
  • Error handling with pcall() for robust execution
  • Caching strategies for downloaded content

Cross-Language Development: Worked across multiple languages:

  • XML for UI definition
  • Lua for extension logic
  • PowerShell for automation
  • Python for testing
  • Bash for build scripts

Development Methodology

Spec-Driven Development: This was my first time using a formal spec-driven approach, and it was transformative:

  • Breaking down requirements into acceptance criteria forced me to think through edge cases upfront
  • Defining correctness properties helped identify what actually mattered
  • Having a clear task list prevented scope creep and kept me focused
  • The spec served as living documentation that stayed in sync with the code

Property-Based Testing: Learned to think in terms of properties rather than examples:

  • "For any Button element, it should have both up and over attributes"
  • "For any Bitmap element, the file path should start with 'default/'"
  • This approach catches entire classes of bugs rather than specific instances

AI-Assisted Development: Discovered how to effectively collaborate with AI:

  • Providing clear context and constraints leads to better results
  • Iterative refinement works better than trying to get everything perfect first
  • AI excels at boilerplate and structure, humans excel at creative decisions
  • Documentation is just as important as code

Challenges I Faced

Challenge 1: VLC Skins2 Documentation

Problem: VLC's Skins2 documentation is sparse and outdated. Many examples online are for VLC 2.x, and the DTD specification is difficult to parse.

Solution: I used Kiro to analyze existing VLC skin files, extract patterns, and generate a working theme.xml. We iterated through multiple versions, testing each in VLC until we got the structure right. The spec-driven approach helped because we could define what we wanted (buttons, sliders, etc.) without knowing the exact XML syntax upfront.

Challenge 2: Lua Extension Auto-Loading

Problem: VLC extensions don't auto-load by default. Users have to manually activate them through the Extensions menu, which defeats the "haunted software" concept.

Solution: After researching VLC's Lua interface system, I discovered the --lua-intf command-line parameter. I created a custom Lua interface (spectre_loader.lua) that uses dofile() to load and activate() the extension automatically. This required understanding VLC's interface loading mechanism and the difference between --intf, --extraintf, and --lua-intf.

Challenge 3: API Integration in Lua 5.1

Problem: VLC uses Lua 5.1, which lacks many modern features:

  • No native JSON parsing
  • Limited HTTP client capabilities
  • No async/await for non-blocking requests

Solution: I implemented manual JSON parsing using string patterns and Lua's pattern matching. For HTTP requests, I used VLC's vlc.stream() API with proper error handling via pcall(). The extension runs in a loop with vlc.misc.mwait() for timing, which blocks but is acceptable for this use case.

Challenge 4: Windows Build Environment

Problem: Compiling VLC on Windows requires MSYS2, MinGW-w64, and numerous dependencies. The build process is complex and poorly documented for Windows.

Solution: I created a comprehensive build script (build_vlc.sh) that:

  • Checks for MSYS2 installation
  • Installs required dependencies via pacman
  • Verifies Spectre files are in place
  • Runs configure with correct flags
  • Handles common build errors

I also provided alternative testing methods (VLT package, manual file copying) for users who don't want to compile from source.

Challenge 5: Balancing Functionality and Horror

Problem: Making the UI too corrupted would break usability. Making it too subtle would lose the horror effect.

Solution: I implemented three levels of corruption:

  1. Subtle (always visible): Play button +5px offset, volume slider width mismatch - noticeable but not breaking
  2. Moderate (random): Scary images and sounds - startling but temporary
  3. Hidden (potential): Full-screen overlay panel - available for future enhancements

This layered approach creates unease without making the player unusable.

Built With

Share this project:

Updates