kowloon

Kowloon

Kowloon is a WebGL playground that mixes procedural city building, spider-like traversal, and lightweight multiplayer networking. The browser loads an immersive Three.js scene backed by a Node.js HTTPS/WebSocket server that synchronises player transforms and chat.

Features

A high-level visual demo is available at https://manthrax.github.io/kowloon/.

Getting started

  1. Install Node.js 18+ and npm.
  2. Install dependencies (only required if node_modules/ is missing):
    npm install
    
  3. Create a local self-signed certificate (stored at key.pem/cert.pem) if you do not already have one:
    npm run cert
    
  4. Launch the development server:
    npm start
    
  5. Open https://localhost:3030/index.html for the single-player sandbox or https://localhost:3030/index-multi.html to exercise the multiplayer HUD.

The WebSocket client defaults to wss://136.24.178.125:3030. For local testing, adjust the URL inside public/client.js.

Project layout

.
├── README.md                – Project overview and operations guide
├── AGENTS.md                – Contributor notes, design synopsis, and testing expectations
├── server.mjs               – Express HTTPS server plus WebSocket message router
├── bandwidth-mon.js         – CLI throughput monitor invoked by `server.mjs`
├── public/                  – Browser application code, assets, and entry HTML
│   ├── app.js               – Application bootstrapper and render loop singleton
│   ├── arachnid.js          – Game mode that loads the city, spider rig, and player logic
│   ├── fps.js               – Character controller, octree collision system, and grapple logic
│   ├── network.js           – Multiplayer hooks built on `client.js` events
│   ├── post-processing.js   – Post-processing pipeline wiring
│   ├── chat.js, utils.js    – UI overlays and helper utilities
│   ├── lib/                 – Reusable helpers (geometry, meshes, post effects, utils)
│   ├── assets (.glb/.hdr)   – Models and environment maps used by the scene
│   └── index*.html          – HTML shells that import the ES modules via an import map
├── docs/
│   └── delaunay.md          – Usage notes for the triangulation helper in `public/lib/geometry`
├── package.json             – npm metadata and scripts (`start`, `cert`)
└── package-lock.json        – Dependency lockfile

Runtime design

Browser application

Server

server.mjs runs an Express app that serves the static public/ bundle over HTTPS (required for WebXR/mouse-lock APIs) and hosts a ws WebSocket server. When each socket connects the server:

  1. Assigns a 24-bit client ID via a 4-byte binary message tagged with opcode 0xA1.
  2. Rewrites the first 4 bytes of every received binary packet to include the sender ID, preventing client spoofing.
  3. Rebroadcasts movement snapshots to all other clients and forwards chat payloads.
  4. Tracks instantaneous bandwidth with BandwidthMon to report aggregate send/receive rates in the console.

Networking API

All WebSocket traffic is binary. The most significant byte of the first 32-bit little-endian word stores an opcode; the remaining 24 bits store the client ID assigned by the server.

Opcode Direction Payload layout Description
0xA1 Server → Client [u24 id][u8 0xA1] (4 bytes total) Sent immediately after connection to assign the receiver’s ID.
0x00 Client ↔ Server id header + 7 × float32 + int16 (34 bytes total) Transform snapshot: position (px,py,pz), quaternion (qx,qy,qz,qw), and control bits (ctr). The server rewrites the header to the authoritative sender ID before broadcasting to peers.
0xA2 Client ↔ Server id header + UTF-8 bytes Chat message. Text such as /ident <color> <display name> is parsed client-side to update player identity cards.

Chat commands processed by public/network.js include:

Snapshots are throttled to roughly 10 Hz (networkInterval = 100ms). The local client only transmits when position or rotation deltas exceed updateResolution to reduce bandwidth. Remote players interpolate between the two most recent frames for smooth motion.

Documentation and references

Contributing