Random number generation in Multisynq is designed to maintain perfect synchronization while giving you flexibility where you need it. Understanding how randomness works in models versus views is crucial for building applications that stay synchronized across all users.

Core Concept

Models: Math.random() produces identical sequences across all devices for perfect synchronization.

Views: Math.random() produces different sequences on each device for local variety.

Models use deterministic random sequences

class GameModel extends Multisynq.Model {
    init() {
        this.enemies = [];
        this.powerUps = [];
        
        // Start spawning systems
        this.future(2000).spawnEnemy();
        this.future(5000).spawnPowerUp();
    }
    
    spawnEnemy() {
        // ✅ This random call is synchronized across all devices
        const x = Math.random() * 800; // Same value on all devices
        const y = Math.random() * 600; // Same value on all devices
        const type = Math.random() < 0.3 ? "fast" : "normal"; // Same decision
        
        const enemy = Enemy.create({
            position: { x, y },
            type: type,
            health: 100
        });
        
        this.enemies.push(enemy);
        
        // Notify views of new enemy
        this.publish("game", "enemy-spawned", {
            id: enemy.id,
            position: enemy.position,
            type: enemy.type
        });
        
        // Schedule next spawn
        const nextSpawnDelay = 1000 + (Math.random() * 3000); // Synchronized delay
        this.future(nextSpawnDelay).spawnEnemy();
    }
    
    spawnPowerUp() {
        // ✅ All random decisions are identical across devices
        if (Math.random() < 0.4) { // Same probability check result
            const powerUpTypes = ["health", "speed", "damage", "shield"];
            const typeIndex = Math.floor(Math.random() * powerUpTypes.length);
            const selectedType = powerUpTypes[typeIndex]; // Same selection
            
            const powerUp = {
                id: this.generateId(),
                type: selectedType,
                position: {
                    x: 50 + Math.random() * 700, // Same position
                    y: 50 + Math.random() * 500
                },
                duration: 10000 + Math.random() * 5000 // Same duration
            };
            
            this.powerUps.push(powerUp);
            this.publish("game", "powerup-spawned", powerUp);
        }
        
        this.future(5000).spawnPowerUp();
    }
    
    handleCombat(attackerId, defenderId) {
        const attacker = this.getEntity(attackerId);
        const defender = this.getEntity(defenderId);
        
        if (attacker && defender) {
            // ✅ Critical hit calculation is synchronized
            const criticalChance = 0.15;
            const isCritical = Math.random() < criticalChance; // Same result everywhere
            
            const baseDamage = attacker.damage;
            const finalDamage = isCritical ? baseDamage * 2 : baseDamage;
            
            defender.health -= finalDamage;
            
            this.publish("game", "combat-result", {
                attackerId,
                defenderId,
                damage: finalDamage,
                isCritical,
                defenderHealth: defender.health
            });
            
            if (defender.health <= 0) {
                this.handleEntityDeath(defender);
            }
        }
    }
    
    generateDrops(entity) {
        // ✅ Loot generation is synchronized
        const drops = [];
        
        // Each item has a chance to drop
        const lootTable = [
            { item: "gold", chance: 0.8, amount: () => 10 + Math.floor(Math.random() * 20) },
            { item: "potion", chance: 0.3, amount: () => 1 },
            { item: "gem", chance: 0.1, amount: () => 1 },
            { item: "rare_weapon", chance: 0.05, amount: () => 1 }
        ];
        
        for (const loot of lootTable) {
            if (Math.random() < loot.chance) { // Same drop decision
                drops.push({
                    item: loot.item,
                    amount: loot.amount() // Same amount
                });
            }
        }
        
        return drops;
    }
}

GameModel.register("GameModel");

Every call to Math.random() in the model produces the exact same value on all devices, ensuring perfect synchronization.

Practical Examples

Best Practices

🎯 Model Randomness

Use for synchronized game logic

// ✅ Good model random usage
class GameModel extends Multisynq.Model {
    // Game mechanics
    rollDice(sides) {
        return Math.floor(Math.random() * sides) + 1;
    }
    
    // Procedural generation
    generateLevel() {
        const layout = [];
        for (let i = 0; i < 100; i++) {
            layout.push(Math.random() < 0.3 ? "wall" : "floor");
        }
        return layout;
    }
    
    // AI decisions
    makeAIChoice(options) {
        return options[Math.floor(Math.random() * options.length)];
    }
}

🎨 View Randomness

Use for visual variety only

// ✅ Good view random usage
class GameView extends Multisynq.View {
    // Visual effects
    createExplosion(pos) {
        for (let i = 0; i < 50; i++) {
            this.addParticle({
                x: pos.x + (Math.random() - 0.5) * 20,
                y: pos.y + (Math.random() - 0.5) * 20,
                color: `hsl(${Math.random() * 60}, 100%, 60%)`
            });
        }
    }
    
    // Animation timing
    startIdleAnimation() {
        const delay = 1000 + Math.random() * 2000;
        setTimeout(() => this.playIdleAnim(), delay);
    }
}

Common Mistakes

Avoid these random number generation errors:

Advanced Patterns

Next Steps

Random number generation in Multisynq is a powerful feature that ensures perfect synchronization when used correctly. Remember: models get identical random sequences for game logic synchronization, while views get independent random sequences for visual variety. Use this distinction to build engaging, synchronized multiplayer experiences.