Simulation Time vs Real Time
- 🕒 Simulation Time
- ⏰ 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
Copy
Ask AI
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.
Local system time used by views
- Source: Local device system clock
- Synchronization: Different on each device
- Variable: Can drift between devices
- Access:
Date.now()in views
Copy
Ask AI
class GameView extends Multisynq.View {
init() {
console.log("View created at:", Date.now()); // Real time
this.updateDisplay();
}
updateDisplay() {
const realTime = Date.now();
const simTime = this.model.now();
console.log("Real time:", realTime);
console.log("Sim time:", simTime);
// Views can use future() with real time
this.future(16).updateDisplay(); // ~60fps
}
}
Never use
Date.now() in models - it breaks synchronization!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
Copy
Ask AI
// 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
Copy
Ask AI
// 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.- Basic Syntax
- Multiple Schedules
- Fractional Delays
Schedule a method to run in the future
Copy
Ask AI
this.future(delay).methodName(args);
- delay: Milliseconds to wait (can be fractional)
- methodName: Method to call when delay expires
- args: Optional arguments to pass
Copy
Ask AI
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();
}
}
Models can have multiple future calls active
Copy
Ask AI
class GameEntity extends Multisynq.Model {
init() {
this.health = 100;
this.position = { x: 0, y: 0 };
// Multiple timing systems
this.updatePosition(); // 60fps movement
this.checkCollisions(); // 20fps collision detection
this.regenerateHealth(); // Every 5 seconds
}
updatePosition() {
// High-frequency position updates
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
this.future(1000/60).updatePosition();
}
checkCollisions() {
// Lower-frequency collision detection
this.detectCollisionsWithOtherEntities();
this.future(1000/20).checkCollisions();
}
regenerateHealth() {
// Periodic health regeneration
if (this.health < 100) {
this.health = Math.min(100, this.health + 1);
}
this.future(5000).regenerateHealth();
}
}
Precise timing with decimal values
Copy
Ask AI
class PreciseTimer extends Multisynq.Model {
init() {
this.frame = 0;
// Exact frame rates
this.render60fps(); // 16.666... ms
this.render30fps(); // 33.333... ms
this.physics120fps(); // 8.333... ms
}
render60fps() {
this.frame++;
this.future(1000/60).render60fps(); // 16.67ms
}
render30fps() {
this.updateSlowSystems();
this.future(1000/30).render30fps(); // 33.33ms
}
physics120fps() {
this.updatePhysics();
this.future(1000/120).physics120fps(); // 8.33ms
}
}
Common Patterns
🎮 Game Loop
🎮 Game Loop
Classic game update pattern
Copy
Ask AI
class GameModel extends Multisynq.Model {
init() {
this.gameState = "playing";
this.entities = new Map();
this.lastUpdate = this.now();
// Start the main game loop
this.gameLoop();
}
gameLoop() {
const currentTime = this.now();
const deltaTime = currentTime - this.lastUpdate;
// Update all game systems
this.updatePhysics(deltaTime);
this.updateAI(deltaTime);
this.updateAnimations(deltaTime);
this.checkWinConditions();
this.lastUpdate = currentTime;
// Schedule next frame (60fps)
this.future(1000/60).gameLoop();
}
updatePhysics(dt) {
for (const entity of this.entities.values()) {
entity.update(dt);
}
}
updateAI(dt) {
// AI logic runs at game loop frequency
}
updateAnimations(dt) {
// Smooth animation updates
}
}
⏰ Timer System
⏰ Timer System
Countdown timers and delayed actions
Copy
Ask AI
class TimerModel extends Multisynq.Model {
init() {
this.timers = new Map();
this.nextTimerId = 1;
}
createTimer(duration, callback, repeat = false) {
const timerId = this.nextTimerId++;
const timer = {
id: timerId,
duration,
callback,
repeat,
startTime: this.now()
};
this.timers.set(timerId, timer);
this.scheduleTimer(timer);
return timerId;
}
scheduleTimer(timer) {
this.future(timer.duration)[timer.callback]();
if (timer.repeat) {
// Reschedule for repeated timers
this.future(timer.duration).rescheduleTimer(timer.id);
}
}
rescheduleTimer(timerId) {
const timer = this.timers.get(timerId);
if (timer && timer.repeat) {
this.scheduleTimer(timer);
}
}
cancelTimer(timerId) {
this.timers.delete(timerId);
// Note: Cannot cancel already scheduled future calls
}
// Example usage
startGame() {
// Game ends in 5 minutes
this.createTimer(5 * 60 * 1000, "endGame", false);
// Spawn enemies every 10 seconds
this.createTimer(10000, "spawnEnemy", true);
}
endGame() {
this.gameState = "finished";
this.publish("game", "ended", { time: this.now() });
}
spawnEnemy() {
const enemy = Enemy.create();
this.entities.set(enemy.id, enemy);
}
}
🏃 Animation System
🏃 Animation System
Smooth object animations
Copy
Ask AI
class AnimatedObject extends Multisynq.Model {
init() {
this.position = { x: 0, y: 0 };
this.target = { x: 100, y: 100 };
this.animationDuration = 2000; // 2 seconds
this.animationStart = null;
this.startAnimation();
}
startAnimation() {
this.animationStart = this.now();
this.animate();
}
animate() {
const elapsed = this.now() - this.animationStart;
const progress = Math.min(elapsed / this.animationDuration, 1);
// Smooth easing function
const easedProgress = this.easeInOutCubic(progress);
// Interpolate position
this.position.x = this.lerp(0, this.target.x, easedProgress);
this.position.y = this.lerp(0, this.target.y, easedProgress);
// Notify views of position change
this.publish("animation", "position", {
id: this.id,
position: this.position,
progress: progress
});
if (progress < 1) {
// Continue animation next frame
this.future(1000/60).animate();
} else {
// Animation complete
this.onAnimationComplete();
}
}
easeInOutCubic(t) {
return t < 0.5
? 4 * t * t * t
: 1 - Math.pow(-2 * t + 2, 3) / 2;
}
lerp(start, end, progress) {
return start + (end - start) * progress;
}
onAnimationComplete() {
this.publish("animation", "complete", { id: this.id });
}
}
Views and Time
Views use real time, not simulation time!
- View Future
- Synchronization
Views can use future() with real time
Copy
Ask AI
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);
}
}
Coordinate between sim time and real time
Copy
Ask AI
class SmoothView extends Multisynq.View {
init() {
this.interpolatedPositions = new Map();
this.renderLoop();
// Listen to model updates
this.subscribe("object", "moved", this.onObjectMoved);
}
onObjectMoved(data) {
// Store position with simulation time
const interpolation = {
startPos: this.interpolatedPositions.get(data.id) || data.position,
endPos: data.position,
startTime: Date.now(),
simTime: this.model.now()
};
this.interpolatedPositions.set(data.id, interpolation);
}
renderLoop() {
const realTime = Date.now();
// Smooth interpolation between model updates
for (const [id, interp] of this.interpolatedPositions) {
const elapsed = realTime - interp.startTime;
const progress = Math.min(elapsed / 100, 1); // 100ms interpolation
const smoothPos = {
x: this.lerp(interp.startPos.x, interp.endPos.x, progress),
y: this.lerp(interp.startPos.y, interp.endPos.y, progress)
};
this.drawObject(id, smoothPos);
}
this.future(16).renderLoop(); // ~60fps
}
}
Timing Best Practices
⚡ Performance
Optimize timing frequency
Copy
Ask AI
// ✅ 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
Copy
Ask AI
// ✅ Good: Batch related operations
this.future(100).batchUpdate();
batchUpdate() {
this.updateMultipleThings();
this.processAllChanges();
this.future(100).batchUpdate();
}
🎯 Precision
Use appropriate timing precision
Copy
Ask AI
// ✅ 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
- Time Logging
- Performance Monitoring
Debug timing problems
Copy
Ask AI
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
}
}
Track timing performance
Copy
Ask AI
class PerformanceModel extends Multisynq.Model {
init() {
this.performanceStats = {
frameCount: 0,
totalTime: 0,
maxFrameTime: 0,
minFrameTime: Infinity
};
this.monitorPerformance();
}
monitorPerformance() {
const frameStart = this.now();
// Do your frame work here
this.updateGame();
const frameEnd = this.now();
const frameTime = frameEnd - frameStart;
// Update statistics
this.performanceStats.frameCount++;
this.performanceStats.totalTime += frameTime;
this.performanceStats.maxFrameTime = Math.max(
this.performanceStats.maxFrameTime,
frameTime
);
this.performanceStats.minFrameTime = Math.min(
this.performanceStats.minFrameTime,
frameTime
);
// Log stats every 60 frames
if (this.performanceStats.frameCount % 60 === 0) {
this.logPerformanceStats();
}
this.future(1000/60).monitorPerformance();
}
logPerformanceStats() {
const stats = this.performanceStats;
const avgFrameTime = stats.totalTime / stats.frameCount;
console.log("Performance Stats:");
console.log(` Average frame time: ${avgFrameTime.toFixed(2)}ms`);
console.log(` Max frame time: ${stats.maxFrameTime.toFixed(2)}ms`);
console.log(` Min frame time: ${stats.minFrameTime.toFixed(2)}ms`);
}
}
Common Pitfalls
Avoid these timing mistakes:
- ❌ Real Time in Models
- ❌ Future Cancellation
- ❌ Blocking Operations
Copy
Ask AI
// ❌ 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();
}
}
Copy
Ask AI
// ❌ Cannot cancel future calls
class ProblematicModel extends Multisynq.Model {
startTimer() {
const futureCall = this.future(5000).doSomething();
// This doesn't work - cannot cancel!
// clearTimeout(futureCall); // No such method
// futureCall.cancel(); // No such method
}
// ✅ Use flags for cancellation
startTimer() {
this.timerActive = true;
this.future(5000).conditionalAction();
}
conditionalAction() {
if (this.timerActive) {
this.doSomething();
}
}
cancelTimer() {
this.timerActive = false; // Prevents execution
}
}
Copy
Ask AI
// ❌ Don't block the main thread
class BlockingModel extends Multisynq.Model {
tick() {
// This blocks synchronization!
for (let i = 0; i < 1000000; i++) {
this.heavyCalculation(i);
}
this.future(16).tick();
}
}
// ✅ Break work into chunks
class NonBlockingModel extends Multisynq.Model {
init() {
this.workQueue = [];
this.processWork();
}
processWork() {
// Process only a small chunk each frame
const startTime = this.now();
while (this.workQueue.length > 0 && this.now() - startTime < 5) {
const work = this.workQueue.shift();
this.processWorkItem(work);
}
this.future(16).processWork();
}
}
Next Steps
Events & Pub-Sub
Learn how events work with time-based systems
Writing a Multisynq Model
Master model development with proper timing
View Smoothing
Create smooth animations between model updates
Random
Understand synchronized random number generation
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.