← Back to all projects

Five Nights At Teller's

Horror Game Featuring my Dog

Motivation

I tend to have this dangerous habit involving projects and the time at which I choose to write them. Specifically, I get really into a project right when I'm about to have a handful of exams. This previous midterm batch was no exception.

I found myself getting increasingly interested in the usage of Webassembly for creating advanced state machines that would then be exposed through a simple API into a static HTML frontend.

At the same time as this, I wanted to make something spooky for Halloween. Lastly, and most importantly, I was really missing my dog this past semester. All of these seemed to accumulate into a perfect storm of project inspiration:

I should create Five Night's at Freddy's in the browser where all enemies are my dogs

The original Five Nights at Freddy's is a horror game where you play as a security guard trying to survive against murderous animatronics using limited power for security cameras and doors. I wanted to capture that tense, resource-management gameplay while adding my own twist: procedurally generated office layouts and AI behaviors that reflect my dogs' actual personalities.

Implementation

The core of the game is a Rust state machine compiled to WebAssembly, with a clean API exposed to a JavaScript frontend. The architecture centers around several key components:

Game State Management

The GameState struct manages the core game loop, tracking time, power consumption, door states, and victory conditions. Each tick() advances the game, reduces power based on current usage (cameras and doors), and checks for win/loss conditions.

Enemy AI System

Enemies implement an EnemyBehavior trait that defines how they act each turn. This allowed me to create distinct personalities for each "dog" enemy. For example, Frank (my cockapoo) wanders randomly through the office, while Teller uses more strategic pathfinding to hunt the player like the predator he is.

The behavior system returns Action enums that can include movement, attacks, or special abilities, allowing for complex enemy behaviors that make each playthrough unique. Action can also include a nested list of Actions, causing complex emergent behavior from enemies that can do multiple or conditional things each movement

Procedural Map Generation

Unlike the fixed layout of the original game, I implemented randomized office generation using graph structures. The map is built as a collection of rooms connected by hallways, with the player's office as the root node. The generation algorithm ensures:

WebAssembly Integration

Using wasm-bindgen, the Rust game logic was easily exposed to JavaScript with minimal overhead. The state machine runs efficiently in WASM while the frontend handles rendering and user input, demonstrating the power of WebAssembly for performance-critical web applications.

The entire system uses SlotMap for efficient entity management, making enemy and room lookups fast while avoiding Rust's borrow checker complications that often arise in graph-based game architectures.