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
🕒 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
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.
Basic Syntax
Multiple Schedules
Fractional Delays
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
Classic game update pattern 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
}
}
Countdown timers and delayed actions 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 );
}
}
Smooth object animations 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 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
Time Logging
Performance Monitoring
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:
❌ Real Time in Models
❌ Future Cancellation
❌ Blocking Operations
// ❌ 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.