Unlike traditional approaches, every user runs the same deterministic application logic locally, ensuring identical state across all participants.
How Multisynq Synchronization Works
Deterministic Virtual Machine
Multisynq runs your Model code in a deterministic virtual machine called Teatime . This ensures that given the same initial state and the same sequence of events, every user’s device produces exactly the same result.
Key Principles
Deterministic Execution Same inputs always produce same outputs across all clients
Event Ordering Reflector servers order all events into a single canonical stream
Synchronized Time Global heartbeat ensures time-dependent operations stay in sync
No Server Logic Reflectors only pass messages - all logic runs on clients
Architecture Overview
User A Device Reflector Server User B Device
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ View A │ │ │ │ View B │
│ ↕ │ │ Orders │ │ ↕ │
│ Model A │ ←── Events ──│ Events │── Events ──→ │ Model B │
│ (Teatime VM)│ │ │ │ (Teatime VM)│
└─────────────┘ └─────────────┘ └─────────────┘
All Models run identically on every device, synchronized through ordered events.
Basic Synchronization Example
Model-Driven State Changes
class CounterModel extends Multisynq . Model {
init () {
this . count = 0 ;
// Subscribe to events from any view
this . subscribe ( this . sessionId , "increment" , this . handleIncrement );
this . subscribe ( this . sessionId , "reset" , this . handleReset );
}
handleIncrement () {
// This runs identically on every user's device
this . count += 1 ;
// Notify all views of the change
this . publish ( this . sessionId , "countChanged" , this . count );
}
handleReset () {
this . count = 0 ;
this . publish ( this . sessionId , "countChanged" , this . count );
}
}
View Interaction
class CounterView extends Multisynq . View {
constructor ( model ) {
super ( model );
// Listen for model changes
this . subscribe ( this . sessionId , "countChanged" , this . updateDisplay );
// Set up UI event handlers
this . setupButtons ();
// Display initial state
this . updateDisplay ( model . count );
}
setupButtons () {
document . getElementById ( "increment" ). onclick = () => {
// Send event to model (will sync to all users)
this . publish ( this . sessionId , "increment" );
};
document . getElementById ( "reset" ). onclick = () => {
this . publish ( this . sessionId , "reset" );
};
}
updateDisplay ( count ) {
document . getElementById ( "counter" ). textContent = count ;
}
}
Advanced Synchronization Patterns
Chat System with Message History
class ChatModel extends Multisynq . Model {
init () {
this . messages = [];
this . subscribe ( this . sessionId , "sendMessage" , this . addMessage );
}
addMessage ({ text , username }) {
// Deterministic ID generation
const message = {
id: this . random (). toString (), // Synchronized random
text: text ,
username: username ,
timestamp: this . now () // Synchronized time
};
this . messages . push ( message );
this . publish ( this . sessionId , "messagesUpdated" , this . messages );
}
}
Real-time Cursor Tracking
class CursorModel extends Multisynq . Model {
init () {
this . cursors = new Map ();
this . subscribe ( this . sessionId , "view-join" , this . userJoined );
this . subscribe ( this . sessionId , "view-exit" , this . userLeft );
this . subscribe ( this . sessionId , "cursorMove" , this . updateCursor );
}
userJoined ( viewId ) {
this . cursors . set ( viewId , { x: 0 , y: 0 , color: this . getRandomColor () });
this . publish ( this . sessionId , "cursorsChanged" , this . getCursorsArray ());
}
userLeft ( viewId ) {
this . cursors . delete ( viewId );
this . publish ( this . sessionId , "cursorsChanged" , this . getCursorsArray ());
}
updateCursor ({ viewId , x , y }) {
if ( this . cursors . has ( viewId )) {
this . cursors . get ( viewId ). x = x ;
this . cursors . get ( viewId ). y = y ;
this . publish ( this . sessionId , "cursorsChanged" , this . getCursorsArray ());
}
}
getRandomColor () {
const colors = [ 'red' , 'blue' , 'green' , 'purple' , 'orange' ];
return colors [ Math . floor ( this . random () * colors . length )];
}
getCursorsArray () {
return Array . from ( this . cursors . entries ()). map (([ viewId , cursor ]) => ({
viewId ,
... cursor
}));
}
}
Synchronized Time and Randomness
Deterministic Time
class GameModel extends Multisynq . Model {
init () {
this . startTime = this . now (); // Synchronized start time
this . gameLoop ();
}
gameLoop () {
const currentTime = this . now ();
const elapsedTime = currentTime - this . startTime ;
// Game logic based on synchronized time
this . updateGameState ( elapsedTime );
// Schedule next update
this . future ( 16 ). gameLoop (); // ~60 FPS
}
}
Deterministic Random Numbers
class LootModel extends Multisynq . Model {
init () {
this . subscribe ( this . sessionId , "openChest" , this . generateLoot );
}
generateLoot () {
// Every user gets the same "random" result
const lootRoll = this . random ();
let loot ;
if ( lootRoll < 0.1 ) {
loot = "legendary" ;
} else if ( lootRoll < 0.3 ) {
loot = "rare" ;
} else {
loot = "common" ;
}
this . publish ( this . sessionId , "lootReceived" , loot );
}
}
Future Scheduling
Use future()
for time-based events that must stay synchronized:
class TimerModel extends Multisynq . Model {
init () {
this . timeLeft = 60 ; // 60 seconds
this . startCountdown ();
}
startCountdown () {
this . publish ( this . sessionId , "timerUpdate" , this . timeLeft );
if ( this . timeLeft > 0 ) {
this . timeLeft -- ;
// Schedule next tick in 1 second
this . future ( 1000 ). startCountdown ();
} else {
this . publish ( this . sessionId , "timerExpired" );
}
}
}
Best Practices for Synchronization
Do’s ✅
Use this.random()
instead of Math.random()
in Models
Use this.now()
instead of Date.now()
in Models
Use this.future()
for time-based operations in Models
Communicate via events between Model and View
Keep Models deterministic - no external API calls, random DOM access, etc.
Don’ts ❌
Never write to Model from View - only through events
Don’t use Math.random()
in Model code
Don’t use Date.now()
or setTimeout()
in Model code
Don’t access DOM from Model code
Don’t make HTTP requests from Model code
Connection Resilience
Built-in Reconnection
Multisynq automatically handles:
Temporary network interruptions
Device sleep/wake cycles
Browser tab switching
Connection drops and recovery
View-Level Connection Awareness
class NetworkView extends Multisynq . View {
constructor ( model ) {
super ( model );
this . isConnected = true ;
}
update ( time ) {
// Check connection status (this is automatic)
const connected = this . session && this . session . view ;
if ( connected !== this . isConnected ) {
this . isConnected = connected ;
this . updateConnectionIndicator ();
}
}
updateConnectionIndicator () {
const indicator = document . getElementById ( "connection-status" );
indicator . textContent = this . isConnected ? "Connected" : "Reconnecting..." ;
indicator . className = this . isConnected ? "connected" : "disconnected" ;
}
}
Testing Synchronization
Multi-User Testing
// Use the Widget Dock for easy multi-device testing
Multisynq . Session . join ({
apiKey: "your-api-key" ,
appId: "com.example.test" ,
model: TestModel ,
view: TestView
}). then ( session => {
// Show QR code for joining from other devices
Multisynq . App . makeWidgetDock ();
});
Debug Synchronization
Multisynq . Session . join ({
// ... other options
debug: [ "session" , "messages" , "events" , "ticks" ]
});
This enables console logging of:
Session connection status
Messages sent/received
Event publishing/subscribing
Heartbeat ticks
Next Steps
Responses are generated using AI and may contain mistakes.