Tez · JavaScript

JavaScript / Web SDK

Browser-native Tez client for web games and apps. Connect to the realtime engine via WebSocket bridge or WASM — same protocol, zero native dependencies.

Overview

The Tez JavaScript SDK lets web applications connect to the Tez realtime engine using the same binary wire protocol as the native clients. It ships as an npm package with full TypeScript definitions and supports both modern frameworks (React, Vue, Svelte) and vanilla JS.

Under the hood, the SDK communicates with the Tez server via a WebSocket-to-UDP bridge or a WASM-compiled client that speaks the identical binary protocol — so every feature (rooms, delta sync, reliable actions, chat, custom events) works the same way as in Unity.

<5KB
Gzipped Size
TypeScript
Full Type Definitions
Any
Framework Support
Same
Wire Protocol as Native

Installation

Terminal
# npm
npm install @cloudfort/tez-sdk
# yarn
yarn add @cloudfort/tez-sdk
# pnpm
pnpm add @cloudfort/tez-sdk
# Or via CDN (browser global)
<script src="https://unpkg.com/@cloudfort/tez-sdk/dist/tez-sdk.min.js"></script>

Quick Start

app.ts
import { TezClient, WebSocketTunnelTransport } from '@cloudfort/tez-sdk';
// 1. Create a transport — the WebSocket tunnel URL points to your bridge
const transport = new WebSocketTunnelTransport({ url: 'ws://your-server:9000/tez' });
// 2. Create the client with config + transport
const client = new TezClient({
room: 'game-lobby', // auto-join after handshake (optional)
token: null, // HMAC token for auth servers, null for dev mode
debug: true, // log connect/disconnect transitions
}, transport);
// 3. Listen for events
client.on('connected', ({ peerId, tickRate }) => {
console.log(`Connected as ${peerId}, tick=${tickRate}`);
});
client.on('snapshot', ({ tick, deltas }) => {
for (const d of deltas) {
if (d.pos) console.log(`peer ${d.peer}: (${d.pos.x}, ${d.pos.y})`);
}
});
// 4. Connect (returns a Promise)
const { peerId, tickRate } = await client.connect();

API Reference

WebSocketTunnelTransport

Transport layer that bridges WebSocket connections to the Tez UDP server.

new WebSocketTunnelTransport({ url })

Create a transport. url is the WebSocket tunnel endpoint (e.g. 'ws://server:9000/tez').

TezClient

Main client class. Takes a config object and a transport instance.

new TezClient(config, transport)

Create a client. Config: room (optional), token, debug. Transport: WebSocketTunnelTransport instance.

connect()

Start connection handshake. Returns Promise<{ peerId, tickRate }>. Auto-joins room if configured.

disconnect()

Gracefully close the connection and clean up resources.

sendInput(vx, vy, facing)Unreliable

Unreliable movement input. Send at your input rate (e.g. 30fps).

sendAction(kind, target)Reliable

Reliable gameplay action. Target 0 broadcasts; non-zero targets a peer.

sendChat(text)Reliable

Reliable chat message to all room members (sender excluded).

sendData(kind, payload)Reliable

Reliable structured data event. Payload is any JSON-serializable object.

world

Read-only world mirror (Map). Updated on every snapshot delta. Use world.get(peerId).

peerId

Own peer ID (0 while not connected).

Events

Subscribe via client.on(event, callback). All callbacks are asynchronous.

'connected'

Handshake accepted. Auto-join initiated if room configured.

({ peerId, tickRate }) => void
'joinSucceeded'

Room join confirmed.

({ room, name }) => void
'snapshot'

State delta received. World mirror updated.

({ tick, deltas }) => void
'data'

Structured data event from another peer (sendData).

({ peer, kind, payload }) => void
'chat'

Chat message from another room member.

({ peer, text }) => void
'disconnected'

Connection lost or stopped.

({ reason, detail }) => void

World Mirror

Read-only state mirror updated on every snapshot delta. Access via client.world.

client.world.get(peerId)

Get a player's current state by peer ID. Returns undefined if not found.

PropertyTypeDescription
peernumberUnique peer ID assigned by the server
position{ x, y }Current position (from server delta)
velocity{ x, y }Current velocity (last submitted input)
facingnumberFacing angle in radians

Framework Integration

useTez.ts — React Hook
import { useState, useEffect, useRef } from 'react';
import { TezClient, WebSocketTunnelTransport } from '@cloudfort/tez-sdk';
export function useTez(url: string, room: string) {
const [world, setWorld] = useState(new Map());
const [peerId, setPeerId] = useState(0);
const [connected, setConnected] = useState(false);
const clientRef = useRef<TezClient>();
useEffect(() => {
const transport = new WebSocketTunnelTransport({ url });
const c = new TezClient({ room, debug: true }, transport);
c.on('connected', ({ peerId }) => {
setPeerId(peerId);
setConnected(true);
});
c.on('snapshot', () => setWorld(new Map(c.world)));
c.on('disconnected', () => setConnected(false));
c.connect();
clientRef.current = c;
return () => c.disconnect();
}, [url, room]);
return { world, peerId, connected, client: clientRef.current };
}
game.js — Movement & Actions
import { TezClient, WebSocketTunnelTransport } from '@cloudfort/tez-sdk';
const transport = new WebSocketTunnelTransport({ url: 'ws://localhost:9000/tez' });
const client = new TezClient({ room: 'game-room' }, transport);
// Send movement at 30fps
setInterval(() => {
const vx = keys['d'] - keys['a'];
const vy = keys['s'] - keys['w'];
const facing = Math.atan2(vy, vx);
client.sendInput(vx * speed, vy * speed, facing);
}, 33);
// Reliable action
client.sendAction(1, targetPeerId);
// Chat
client.sendChat('Hello from the browser!');
// Structured data (Custom)
client.sendData(1, { move: 'e3' });
// Read world mirror
client.on('snapshot', ({ tick, deltas }) => {
const me = client.world.get(client.peerId);
if (me) console.log(me.position); // { x, y }
});
await client.connect();

Connection Lifecycle

The JS client follows the same state machine as the native clients. After calling connect(), the client sends Hello packets, receives Welcome with a peer ID, joins the specified room, and begins receiving state deltas.

If the connection drops, the client automatically attempts reconnection with exponential backoff. Cluster redirects are handled transparently — the client follows the redirect and re-joins the room on the target node.

Handshaking

Hello packets sent. Waiting for Welcome with peer ID.

Joining

Handshake done. JoinRoom request sent, waiting for confirmation.

Connected

Fully operational. Input, state sync, and events are active.

Redirecting

Cluster redirect. Client follows to the owning node automatically.

Reconnecting

Connection lost. Exponential backoff: 400ms → 800ms → 1.6s → ...

Disconnected

Session ended. Voluntary stop, timeout, or redirect loop.

Authentication

For servers with --auth-key configured, pass a 48-byte HMAC-SHA256 token when creating the client. Your backend should mint tokens using the shared secret:

// Node.js backend — mint a token
const crypto = require('crypto');
const nonce = BigInt(Date.now());
const expires = BigInt(Math.floor(Date.now() / 1000) + 3600);
const buf = Buffer.alloc(48);
buf.writeBigUInt64LE(nonce, 0);
buf.writeBigUInt64LE(expires, 8);
crypto.createHmac('sha256', authKeyHex)
.update(buf.slice(0, 16)).digest().copy(buf, 16);
// Pass token to the JS client
new TezClient({ room, token: buf }, transport);

Without an auth key (dev/LAN mode), any token — including null — is accepted.

Features

Zero Dependencies

Pure browser APIs. No jQuery, no Socket.IO — just the Tez binary protocol.

Full TypeScript

Ships with complete .d.ts definitions. Typed events, methods, and data structures.

Auto Reconnect

Built-in reconnection with exponential backoff. Room state is remembered and restored.

Any Framework

Works with React, Vue, Angular, Svelte, or vanilla JS. Framework-agnostic by design.

Same Protocol

Speaks the identical binary wire protocol as native clients. No translation layer needed.

Delta Compression

Only changed fields cross the wire. Minimal bandwidth even with many players.

TypeScript Support

The package ships with complete .d.ts type definitions. All events, methods, and data structures are fully typed for a great developer experience:

import type {
TezClient,
TezClientConfig,
WebSocketTunnelTransport,
Snapshot,
Delta,
WorldMirror
} from '@cloudfort/tez-sdk';

Ready to build?

Tez is currently free. Get your server address and start building realtime web experiences today.