Understanding Multisynq’s time system is crucial for building applications with animations, timers, and time-dependent behaviors. Unlike real-world time, Multisynq uses simulation time to ensure perfect synchronization across all users.

Simulation Time vs Real Time

Synchronized, deterministic time used by models

  • Source: Multisynq reflector heartbeat ticks
  • Synchronization: Identical across all users
  • Deterministic: Same time, same results
  • Access: this.now() in models
class GameModel extends Multisynq.Model {
    init() {
        console.log("Session started at:", this.now()); // Simulation time
        this.tick();
    }
    
    tick() {
        const currentTime = this.now();
        console.log("Simulation time:", currentTime);
        
        // All users see identical time values
        this.future(1000).tick(); // Schedule next tick
    }
}

Simulation time only advances when heartbeat ticks are received from the reflector.

Core Time Concepts

📐 Simulation Time

Time since session began

  • Measured in milliseconds
  • Starts at 0 when session begins
  • Identical for all users
  • Only advances with reflector heartbeats
// All users get the same value
const time = this.now(); // e.g., 45723 ms

💓 Heartbeat Ticks

Synchronization mechanism

  • Sent by reflector to all users
  • Advances simulation time
  • Ensures synchronized execution
  • Typically ~60-120Hz
// Multiple calls return same value
console.log(this.now()); // 1000
console.log(this.now()); // 1000 (same!)
console.log(this.now()); // 1000 (until next tick)

The future() Method

future() is Multisynq’s scheduling mechanism for time-based behaviors.

Schedule a method to run in the future

this.future(delay).methodName(args);
  • delay: Milliseconds to wait (can be fractional)
  • methodName: Method to call when delay expires
  • args: Optional arguments to pass
class AnimationModel extends Multisynq.Model {
    init() {
        this.x = 0;
        this.startAnimation();
    }
    
    startAnimation() {
        this.x += 1;
        
        // Schedule next frame in 16.67ms (~60fps)
        this.future(1000/60).startAnimation();
    }
    
    explode() {
        // Destroy after 2 seconds
        this.future(2000).destroy();
    }
}

Common Patterns

Views and Time

Views use real time, not simulation time!

Views can use future() with real time

class GameView extends Multisynq.View {
    init() {
        this.canvas = document.getElementById('canvas');
        this.lastFrame = Date.now();
        
        // Start render loop using real time
        this.renderLoop();
    }
    
    renderLoop() {
        const now = Date.now();
        const deltaTime = now - this.lastFrame;
        
        // Render current frame
        this.render(deltaTime);
        
        this.lastFrame = now;
        
        // Schedule next frame (~60fps)
        this.future(16).renderLoop();
    }
    
    render(deltaTime) {
        // Use simulation time for game state
        const gameTime = this.model.now();
        
        // Use real time for smooth interpolation
        this.drawGameObjects(gameTime, deltaTime);
    }
}

Timing Best Practices

⚡ Performance

Optimize timing frequency

// ✅ Good: Different rates for different systems
this.updatePhysics();    // 60fps - needs precision
this.updateAI();         // 10fps - less critical
this.updateNetworking(); // 20fps - moderate

// ❌ Avoid: Everything at maximum rate
this.future(16).updateEverything(); // Wasteful
// ✅ Good: Batch related operations
this.future(100).batchUpdate();

batchUpdate() {
    this.updateMultipleThings();
    this.processAllChanges();
    this.future(100).batchUpdate();
}

🎯 Precision

Use appropriate timing precision

// ✅ Good: Match timing to need
this.future(1000/60).renderFrame();   // 60fps for smooth animation
this.future(1000/20).checkCollisions();// 20fps for game logic
this.future(5000).saveProgress();      // 5s for persistence

// ❌ Avoid: Inappropriate frequencies
this.future(1).highFrequencySpam();    // Too fast
this.future(10000).criticalGameLogic(); // Too slow

Debugging Time Issues

Debug timing problems

class DebugModel extends Multisynq.Model {
    init() {
        this.tickCount = 0;
        this.startTime = this.now();
        
        this.debugTick();
    }
    
    debugTick() {
        this.tickCount++;
        const elapsed = this.now() - this.startTime;
        const averageInterval = elapsed / this.tickCount;
        
        console.log(`Tick ${this.tickCount}:`);
        console.log(`  Sim time: ${this.now()}`);
        console.log(`  Elapsed: ${elapsed}ms`);
        console.log(`  Average interval: ${averageInterval.toFixed(2)}ms`);
        
        this.future(1000).debugTick(); // Every second
    }
}

Common Pitfalls

Avoid these timing mistakes:

// ❌ NEVER do this in models
class BadModel extends Multisynq.Model {
    init() {
        this.startTime = Date.now(); // BREAKS SYNCHRONIZATION!
        this.badTick();
    }
    
    badTick() {
        const now = Date.now(); // DIFFERENT ON EACH DEVICE!
        if (now - this.startTime > 5000) {
            this.doSomething(); // Will happen at different times!
        }
        this.future(100).badTick();
    }
}

// ✅ Correct approach
class GoodModel extends Multisynq.Model {
    init() {
        this.startTime = this.now(); // Use simulation time
        this.goodTick();
    }
    
    goodTick() {
        const now = this.now(); // SYNCHRONIZED!
        if (now - this.startTime > 5000) {
            this.doSomething(); // Happens simultaneously!
        }
        this.future(100).goodTick();
    }
}

Next Steps

Mastering simulation time and the future() method is essential for creating responsive, synchronized applications. Always use simulation time in models and real time in views to maintain perfect synchronization across all users.