Puppeteer: Multiplayer Movement & Sync Library (JavaScript)

Puppeteer is a modular, extensible JavaScript library designed to support multiplayer game development using 3D frameworks like Three.js. It includes networking, interpolation, prediction, and movement handling, and is structured to scale up to competitive or simulation-heavy multiplayer games.

Get Started

Installation

Install Puppeteer using npm:

npm install puppeteer-js

Or using a script tag:

<script src="https://cdn.example.com/puppeteer/latest/puppeteer.min.js"></script>

Project Structure

/puppeteer/ ├── core/ - Basic math classes (Vector3, Quaternion, Transform) ├── interp/ - Interpolation, snapshot logic ├── net/ - Networking (client & host) ├── movement/ - Physics & directional movement ├── utils/ - Utility modules (Clock, Logger, UUID) ├── index.js - Library entry point └── sample.html - Example usage with Three.js

Core Concepts

Puppeteer provides a complete system for handling multiplayer interactions in 3D space. Here are the key components:

Vector3 & Quaternion

Core math utilities for 3D space operations.

const vec = new Puppeteer.Vector3(x, y, z)

Creates a new three-dimensional vector with x, y, and z coordinates. Provides methods for vector math operations including addition, subtraction, multiplication, normalization, dot products, and cross products.

// Create and manipulate vectors
const position = new Puppeteer.Vector3(0, 0, 0);
position.add(new Puppeteer.Vector3(1, 2, 3));
const distance = position.distanceTo(new Puppeteer.Vector3(5, 5, 5));
const rotation = new Puppeteer.Quaternion()

Represents rotations in 3D space, avoiding gimbal lock issues common with Euler angles. Used for smooth interpolation between orientations.

// Create and use quaternions for rotation
const rotation = new Puppeteer.Quaternion();
rotation.setFromAxisAngle(new Puppeteer.Vector3(0, 1, 0), Math.PI / 2);
rotation.multiply(anotherQuaternion);

Transform

const transform = new Puppeteer.Transform(position, rotation)

Combines position and rotation into a single object. Represents the complete spatial state of an object in 3D space.

// Create a transform representing position and rotation
const transform = new Puppeteer.Transform(
    new Puppeteer.Vector3(10, 0, 5),
    new Puppeteer.Quaternion().setFromAxisAngle(new Puppeteer.Vector3(0, 1, 0), Math.PI)
);

Snapshot

const snapshot = new Puppeteer.Snapshot(transform, timestamp)

Stores a Transform with a timestamp. Used to record the state of an object at a specific point in time for networking and interpolation.

// Create a snapshot of an object's state at the current time
const snapshot = new Puppeteer.Snapshot(
    playerTransform,
    Puppeteer.Utils.Clock.now()
);

Interpolator

const interpolator = new Puppeteer.Interpolator(options)

Computes smooth transitions between snapshots. Essential for visual smoothing of network updates.

// Create an interpolator with custom settings
const interpolator = new Puppeteer.Interpolator({
    maxSnapshots: 10,
    interpolationDelay: 100 // ms
});

// Add snapshots as they arrive from the network
interpolator.addSnapshot(snapshot1);
interpolator.addSnapshot(snapshot2);

// Get the interpolated transform to render
const renderTransform = interpolator.computeTransformAtTime(Puppeteer.Utils.Clock.now());

RoomClient

const client = new Puppeteer.Net.RoomClient(serverUrl)

Handles WebSocket client networking. Connects to a RoomHost and synchronizes object states.

// Connect to a multiplayer room
const client = new Puppeteer.Net.RoomClient("wss://game.example.com/room/12345");

client.on("connect", () => {
    console.log("Connected to room!");
});

client.on("playerJoined", (playerId) => {
    console.log(`Player ${playerId} joined the room`);
});

// Send player updates
client.sendTransform(myPlayerTransform);

RoomHost

const host = new Puppeteer.Net.RoomHost(options)

Hosts a WebSocket server for multiplayer rooms. Manages connections and object ownership.

// Server-side code to host a room
const host = new Puppeteer.Net.RoomHost({
    port: 8080,
    maxPlayers: 16,
    tickRate: 30
});

host.on("playerJoin", (client) => {
    console.log(`Player joined with ID: ${client.id}`);
});

host.start();

MovementController

const controller = new Puppeteer.Movement.MovementController(transform, options)

Moves a player in a specific direction based on speed and deltaTime. Handles physics-based movement.

// Create a movement controller for a player
const controller = new Puppeteer.Movement.MovementController(playerTransform, {
    speed: 5,
    rotationSpeed: 2,
    gravity: 9.8
});

// Update during game loop
function gameLoop(deltaTime) {
    // Move based on input
    if (keys.forward) {
        controller.moveForward(deltaTime);
    }
    if (keys.right) {
        controller.rotate(deltaTime);
    }
    
    // Apply physics updates
    controller.update(deltaTime);
}

Basic Usage (in Browser)

Here's a simple example of integrating Puppeteer with Three.js:

import Puppeteer from './puppeteer/index.js';

// Set up Three.js scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Create a player with Puppeteer
const playerTransform = new Puppeteer.Transform(
    new Puppeteer.Vector3(0, 0, 0),
    new Puppeteer.Quaternion()
);

// Create Three.js representation
const playerGeometry = new THREE.BoxGeometry(1, 2, 1);
const playerMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const playerMesh = new THREE.Mesh(playerGeometry, playerMaterial);
scene.add(playerMesh);

// Connect to multiplayer room
const client = new Puppeteer.Net.RoomClient("wss://example.com/game");

client.on("playerUpdate", (playerId, playerSnapshot) => {
    if (playerId !== client.id) {
        // Update other player's position (simplified)
        otherPlayers[playerId].addSnapshot(playerSnapshot);
    }
});

// Movement controller
const movement = new Puppeteer.Movement.MovementController(playerTransform);

// Game loop
function animate() {
    requestAnimationFrame(animate);
    
    // Update player movement based on input
    movement.update(0.016); // Approx 60fps
    
    // Apply transform to Three.js mesh
    playerMesh.position.set(
        playerTransform.position.x,
        playerTransform.position.y,
        playerTransform.position.z
    );
    
    playerMesh.quaternion.set(
        playerTransform.rotation.x,
        playerTransform.rotation.y,
        playerTransform.rotation.z,
        playerTransform.rotation.w
    );
    
    // Send updates to server
    client.sendTransform(playerTransform);
    
    // Render scene
    renderer.render(scene, camera);
}

animate();

Advanced Examples

Prediction & Reconciliation

Puppeteer supports client-side prediction and server reconciliation for responsive multiplayer gameplay:

// Client-side prediction
const inputQueue = [];
let lastProcessedInput = 0;

function processInput(input) {
    // Apply input locally
    movement.applyInput(input);
    
    // Save input for reconciliation
    inputQueue.push(input);
    
    // Send to server
    client.sendInput(input);
}

// When server confirms state
client.on("serverState", (serverSnapshot, lastInputId) => {
    // Remove confirmed inputs
    inputQueue = inputQueue.filter(input => input.id > lastInputId);
    
    // Reset to server state
    playerTransform.copy(serverSnapshot.transform);
    
    // Reapply remaining inputs
    for (const input of inputQueue) {
        movement.applyInput(input);
    }
});

API Reference

See the full API reference for detailed documentation of all methods and classes.

Core Module

Interpolation Module

Networking Module

Movement Module

Utilities Module