← Back to all projects

Multiplayer Tower Defense

Real-time Browser-based Tower Defense Game

Game

Sample game between two players, showing the unit reload mechanism and economy

Motivation

This project represented my final deep dive into async Rust and WebSocket protocols. I wanted to tackle a significant technical challenge that would push my understanding of massive communication and concurrent systems. A multiplayer tower defense game presented the perfect opportunity to explore these concepts, as I was really obsessed with The Battle Cats at the time.

Beyond the technical learning objectives, I wanted to create a polished, full-stack application that demonstrated the power of Rust for backend systems and TypeScript for responsive frontend interfaces, all connected through efficient WebSocket communication.

Implementation

The game architecture follows a client-server model with Rust handling the backend game logic and TypeScript managing the frontend rendering and user interactions.

Backend Architecture

The Rust server implements a real-time game engine using async WebSocket connections. One of the most innovative aspects is the dynamic unit loading system. Game units are defined as JSON files in a dedicated units directory, which are processed at compile time using a custom build script. This script reads all unit definitions and embeds them as static strings that are lazily deserialized into Unit structs when first accessed.

In the User struct, every player owns their WebSocket write channel, allowing the server to efficiently broadcast game state updates without complex routing logic. This design enables centralized read handling while maintaining direct, optimized write paths to each connected client.

Game Systems

The game features comprehensive tower defense mechanics including:

Frontend Implementation

The TypeScript frontend uses the canvas for all rendering. It owns one end of the Client's websocket, and responds to enemy spawn events and damage ticks accordingly. Because we're using emojis for everything, that also means we don't need to handle any sprite or animation logic other than applying a cosine function to a text element for each unit.