The thoughts.html page implements a multiplayer 3D bouncing sphere environment. Here's the WebSocket API specification for the backend server:
{
"type": "player_join",
"position": [12.5, 0, -8.3],
"color": [0.8, 0.2, 0.6],
"shape": 0,
"timestamp": 1703123456789
}Note: playerId is no longer sent by client - server will generate and assign
{
"type": "position_update",
"position": [15.2, 0, -10.1],
"timestamp": 1703123456890
}Note: playerId no longer needed - server tracks client by WebSocket connection
{
"type": "shape_update",
"shape": 1,
"timestamp": 1703123456950
}Note: playerId no longer needed - server tracks client by WebSocket connection
{
"type": "player_leave",
"timestamp": 1703123456999
}Note: playerId no longer needed - server detects disconnection automatically
{
"type": "welcome",
"playerId": "xyz789ghi",
"timestamp": 1703123456800
}Note: This is the first message sent to a client after connecting, providing their server-assigned ID (opaque string)
{
"type": "player_join",
"playerId": "xyz789ghi",
"position": [20.0, 0, 15.5],
"color": [0.3, 0.9, 0.4],
"shape": 0,
"timestamp": 1703123457000
}{
"type": "position_update",
"playerId": "xyz789ghi",
"position": [22.1, 0, 16.8],
"timestamp": 1703123457100
}{
"type": "shape_update",
"playerId": "xyz789ghi",
"shape": 2,
"timestamp": 1703123457150
}{
"type": "player_leave",
"playerId": "xyz789ghi",
"timestamp": 1703123457200
}{
"type": "game_state",
"players": [
{
"playerId": "abc123def",
"position": [15.2, 0, -10.1],
"color": [0.8, 0.2, 0.6],
"shape": 1
},
{
"playerId": "xyz789ghi",
"position": [22.1, 0, 16.8],
"color": [0.3, 0.9, 0.4],
"shape": 2
}
],
"timestamp": 1703123457300
}- Player IDs: Assigned by server on connection via "welcome" message
- Position Format:
[x, y, z]where Y is always 0 (ground level) - Color Format:
[r, g, b]as floats 0.0-1.0 - Shape Format: Integer where 0=Sphere, 1=Cube, 2=Pyramid
- Shape Controls: Press spacebar to cycle through shapes
- Throttling: Position updates max every 50ms, min 0.1 unit movement
- World Boundary: ±50 units in X/Z directions
- Generate unique player ID for each new connection (opaque string format)
- Send welcome message with assigned player ID immediately after connection
- Broadcast player_join to all other clients when new player sends initial join
- Relay position_update to all other clients (don't echo back to sender)
- Relay shape_update to all other clients (don't echo back to sender)
- Broadcast player_leave when client disconnects
- Track active players by WebSocket connection and their current positions/shapes
- Optional: Send full game_state to new players after welcome message
- Handle malformed JSON gracefully
- Validate position boundaries (±50 units)
- Validate color values (0.0-1.0 range)
- Validate shape values (0-2 range)
- Clean up players on WebSocket disconnection
The client includes a fake server simulation for local testing. In the browser console, you can use:
testDisconnection()- Manually trigger a bot disconnection
- Client establishes WebSocket connection to server
- Server generates unique player ID and sends "welcome" message
- Client receives welcome message and stores assigned player ID
- Client sends "player_join" message with initial position, color, and shape
- Server broadcasts "player_join" to all other connected clients
- Normal gameplay proceeds with position/shape updates
The multiplayer system was implemented in 5 steps:
- Refactored for multiplayer: Separated game state, player management, and network communication
- Random spawning: Players spawn at random locations with random colors
- WebSocket communication: Position updates sent to server with throttling
- Multi-sphere rendering: Updated WebGL shader to render multiple bouncing spheres
- Disconnection handling: Players can leave and their spheres disappear from the world
The system uses WebGL2 ray tracing for rendering, with each player represented as a bouncing 3D shape (sphere, cube, or pyramid) in a stormy 3D environment. Players can cycle through shapes using the spacebar.