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.
public/lib/meshes/city.js composes modular building meshes so players start on the rooftops of a dense skyline.public/fps.js implements a wall-walking character controller with grappling webs, collision detection via octrees, and first-person camera helpers.public/post-processing.js wires up bloom and other full-screen passes through PostProcessing and the custom Dual Kawase bloom implementation in public/lib/post/DualKawaseBloomPass.js.public/network.js and public/client.js talk to the WebSocket bridge in server.mjs to replicate player transforms, broadcast chat, and assign stable client IDs.A high-level visual demo is available at https://manthrax.github.io/kowloon/.
node_modules/ is missing):
npm install
key.pem/cert.pem) if you do not already have one:
npm run cert
npm start
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 insidepublic/client.js.
.
├── 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
App (in public/app.js) encapsulates the Three.js renderer, camera rig, GUI, post-processing pipeline, and animation loop. It exposes a singleton via App.getApp() so feature modules can reuse the shared context.arachnid.js acts as the main entry point for index.html, assembling the procedural city, HDR environment map, spider rig, and UI overlays. It creates a pool of MetaPlayer instances (from fps.js) for the local player and any remote peers.fps.js supplies the core gameplay systems: an octree-backed collision mesh, capsule physics, grappling/web mechanics, cursor UI panels, and player locomotion on arbitrary surfaces. The module exports helpers like setCollisionMesh, localPlayer, MetaPlayer, and UIPanel that arachnid.js and network.js coordinate.post-processing.js configures bloom and tone mapping and exposes PostProcessing.render() as a drop-in replacement for renderer.render() inside the main loop.public/lib stores reusable building blocks:
geometry/delaunay.js builds clipped Delaunay triangulations and Voronoi cells.meshes/city.js generates kit-bashed building meshes and roads.post/DualKawaseBloomPass.js houses the custom bloom pass.utils/ exports PRNG (alea.js) and geometry simplification tools.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:
0xA1.BandwidthMon to report aggregate send/receive rates in the console.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:
/ident <hexColor> <name> – Broadcasts a color/name combination for HUD labels.reset – Forces connected clients to reload the page.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.
docs/delaunay.md.*.glb, *.hdr) were authored externally and imported into public/.App singleton so they participate in the shared animation loop and GUI event bus.npm start and exercising relevant browser flows.AGENTS.md up-to-date when the layout, runtime architecture, or network protocol evolves.