skip to content

Backend Radar Simulator

[completed]Three.js, WebSocket, Node.js, TypeScript, Reactgithub ↗

Project Overview

Real-time radar simulation system built with TypeScript. A Node.js backend handles aircraft simulation and detection logic; a React frontend renders the world in Three.js. The system simulates aircraft detection within a rotating radar cone, supporting dual view modes (top-down radar and realistic 3D), WebSocket-based real-time updates, and interactive aircraft inspection.

The radar tracks multiple aircraft types — Military, Civilian, Police — each with realistic flight patterns. The frontend offers both a classic radar display and an immersive 3D visualization.

scene loads on this page in phase 3

Technology Stack

| Component | Technology | | ----------------- | ------------------------ | | Backend Runtime | Node.js | | Backend Framework | Express ^5.1.0 | | WebSocket | ws ^8.18.3 | | 3D Math (Backend) | Three.js ^0.178.0 | | Frontend | React ^19.1.0 + Vite | | Frontend 3D | Three.js ^0.179.1 | | Language | TypeScript |

Architecture

The backend runs an Express HTTP server plus a WebSocket server. A singleton WorldState initializes a RadarCone(origin, 400, 120, 100) and 300 Aircraft instances (100 per type × 3 types). Every ~16.7ms (~60 FPS) the server broadcasts radar data to all connected clients.

The frontend's RadarScene boots a Three.js camera + renderer, preloads aircraft instanced meshes, opens the WebSocket, and runs an animation loop at 60 FPS. Aircraft positions update over the wire; the renderer reuses pre-allocated instances from a pool keyed by aircraft type.

Core Algorithms

Aircraft Position (Polar → Cartesian)

function polarToCartesian(
    origin: Vector3,
    distance: number,
    azimuthDeg: number,
    elevationDeg: number
): Vector3 {
    // Converts azimuth + elevation to Cartesian X, Y, Z.
    // Y is altitude; X and Z form the horizontal plane.
}

Radar Cone Detection

private isWithinCone(
    targetAzimuth: number,
    targetDistance: number,
    targetElevation: number
): boolean {
    // Distance: within detectionRange (default 400 units)
    // Azimuth:  within sweepWidth/2 of currentAzimuth
    // Elevation: between minElevation (0°) and maxElevation (100°)
}

Instance Pool Management

The frontend maintains a pool of 100 pre-allocated InstancedMesh slots per aircraft type. As aircraft enter detection range, the renderer pops a free index from the pool, writes the world matrix, and flips the instance visible. When an aircraft leaves range, the index is recycled. This keeps draw calls flat regardless of how many aircraft are in the simulation.

What I Learned

  • Instanced rendering is essential once you cross ~50 dynamic objects in a Three.js scene; per-object meshes will tank your frame rate.
  • Spherical coordinates make radar sweep math trivial once you commit to a single convention (azimuth = horizontal, elevation = vertical) and stick to it on both sides of the wire.
  • WebSockets at ~60 FPS are fine for hundreds of aircraft, but the wire protocol needs to be minimal — JSON-stringifying the whole world every tick is overkill. Diff-only updates would be the next step.