1. Prerequisiti
Per seguire questo tutorial non serve esperienza avanzata. Sono sufficienti conoscenze base di HTML — sapere cos'è un tag, come si struttura una pagina — e di JavaScript, in particolare come funzionano variabili, funzioni ed eventi. Non è necessario usare framework: tutto il codice proposto funziona in un file HTML con uno script inline, apribile direttamente nel browser senza strumenti aggiuntivi.
2. Il punto di partenza: Math.random()
Tutto parte da una funzione nativa di JavaScript: Math.random(). Chiamandola, restituisce un numero decimale pseudocasuale compreso tra 0 (incluso) e 1 (escluso), e il valore cambia a ogni chiamata. Da sola, però, ha un'utilità limitata: quasi nessuno ha bisogno di un numero tra 0 e 0.99. Serve una formula per trasformare quel valore in un numero intero all'interno di un intervallo specifico.
Math.random(); // es. 0.7341029183 Math.random(); // es. 0.1520934710 Math.random(); // es. 0.9912873400
3. Generare numeri in un intervallo
La formula standard per ottenere un numero intero compreso tra min e max — entrambi inclusi — è questa:
Math.floor(Math.random() * (max - min + 1)) + min
Come funziona passo per passo: Math.random() produce un valore tra 0 e 0.99 che viene moltiplicato per l'ampiezza dell'intervallo (max - min + 1), e questo dà un decimale tra 0 e l'ampiezza. Math.floor() lo arrotonda all'intero inferiore — da 0 a (max - min) — e sommando minsi trasla il risultato nell'intervallo desiderato.
Numeri decimali
Se invece dei numeri interi servono valori con cifre decimali, si omette Math.floor() e si usa toFixed(n) per controllare quante cifre dopo la virgola mostrare.
// Numero decimale tra min e max con 2 cifre const val = (Math.random() * (max - min) + min).toFixed(2);
4. Generare numeri unici senza ripetizioni
Per estrazioni come il Lotto — 5 numeri distinti da 1 a 90 — i valori non possono ripetersi. Un approccio robusto è l'algoritmo Fisher-Yates: si crea un array con tutti i valori possibili, lo si mescola in modo statisticamente uniforme e si prendono i primi N elementi. L'algoritmo scorre l'array dall'ultimo elemento al primo e, per ciascuno, scambia la posizione con un elemento scelto a caso tra quelli che lo precedono.
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// 5 numeri unici da 1 a 90
const pool = Array.from({ length: 90 }, (_, i) => i + 1);
const result = shuffle(pool).slice(0, 5);Un'alternativa usa un Set: si generano numeri casuali finché il Set non raggiunge la dimensione richiesta. Per piccoli intervalli e poche estrazioni funziona bene, ma su grandi quantità diventa lenta perché aumenta la probabilità di generare duplicati e rifare il calcolo.
5. Creare l'interfaccia utente
Una struttura HTML minimale richiede un campo input per il minimo, uno per il massimo, un pulsante per avviare la generazione e un contenitore dove mostrare il risultato.
<div class="generator"> <label>Min: <input type="number" id="min" value="1" /></label> <label>Max: <input type="number" id="max" value="100" /></label> <button id="btn">Genera</button> <div id="result">—</div> </div>
Per collegare il pulsante alla logica di generazione si usa un event listener che legge i valori degli input, calcola il numero casuale e lo scrive nel div del risultato.
document.getElementById('btn').addEventListener('click', () => {
const min = parseInt(document.getElementById('min').value);
const max = parseInt(document.getElementById('max').value);
const num = Math.floor(Math.random() * (max - min + 1)) + min;
document.getElementById('result').textContent = num;
});6. Aggiungere l'effetto animazione
Un semplice effetto slot machine — numeri che scorrono prima del risultato finale — si ottiene con setInterval. L'idea è mostrare valori casuali a intervalli rapidi per una quindicina di frame, poi fermarsi sul risultato definitivo. Il numero di frame e la velocità dell'intervallo controllano quanto l'animazione sembra fluida.
function animateResult(min, max, finalValue, element) {
let count = 0;
const totalFrames = 15;
const interval = setInterval(() => {
element.textContent = Math.floor(Math.random() * (max - min + 1)) + min;
count++;
if (count >= totalFrames) {
clearInterval(interval);
element.textContent = finalValue;
}
}, 80);
}Per un effetto più naturale, si aumenta progressivamente il ritardo tra i frame così i numeri rallentano gradualmente fino a fermarsi, simulando l'inerzia di una slot machine fisica.
7. Esempio: generatore per il Lotto
Il Lotto italiano estrae 5 numeri distinti da 1 a 90. Con l'algoritmo Fisher-Yates applicato a un pool di 90 elementi, il codice completo è questo:
function lotto() {
const pool = Array.from({ length: 90 }, (_, i) => i + 1);
for (let i = 89; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[pool[i], pool[j]] = [pool[j], pool[i]];
}
return pool.slice(0, 5).sort((a, b) => a - b);
}
lotto(); // es. [7, 23, 45, 67, 88]Il .sort() finale ordina i numeri in modo crescente prima di mostrarli — come avviene nelle schedine fisiche — ma è una scelta di leggibilità, non una necessità algoritmica.
8. Esempio: generatore di password
Una password sicura combina lettere maiuscole e minuscole, numeri e simboli. Tre regole fondamentali nella costruzione: usare Crypto.getRandomValues() invece di Math.random() per avere sicurezza crittografica, forzare almeno un carattere per ogni categoria e mescolare il risultato con Fisher-Yates così i caratteri obbligatori non compaiono sempre nello stesso punto.
function generatePassword(length = 16) {
const lower = 'abcdefghijklmnopqrstuvwxyz';
const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const digits = '0123456789';
const symbols = '!@#$%^&*()_+-=[]{}|;:,.<>?';
const all = lower + upper + digits + symbols;
const rand = (max) => {
const arr = new Uint32Array(1);
crypto.getRandomValues(arr);
return arr[0] % max;
};
let chars = [
lower[rand(lower.length)],
upper[rand(upper.length)],
digits[rand(digits.length)],
symbols[rand(symbols.length)],
];
while (chars.length < length) {
chars.push(all[rand(all.length)]);
}
for (let i = chars.length - 1; i > 0; i--) {
const j = rand(i + 1);
[chars[i], chars[j]] = [chars[j], chars[i]];
}
return chars.join('');
}9. Esempio: simulatore di dadi
Il dado standard ha 6 facce, ma nei giochi di ruolo si usano dadi poliedrici da 4 a 100 facce. La funzione base è semplice: un parametro per il numero di facce e la formula standard per generare un intero nell'intervallo 1–N.
function rollDice(faces) {
return Math.floor(Math.random() * faces) + 1;
}
rollDice(6); // D6: valore tra 1 e 6
rollDice(20); // D20: valore tra 1 e 20Per la visualizzazione, il dado può essere disegnato in CSS con bordi arrotondati e pallini posizionati in base al valore estratto. Per un effetto realistico si applica una transizione su transform: rotate3d() con un angolo casuale a ogni lancio, così il dado sembra girare prima di mostrare il risultato.
10. Best practice e sicurezza
Quando usare Crypto.getRandomValues()
Math.random() è adeguato per giochi, animazioni e simulazioni dove la prevedibilità non è un rischio reale. Per qualsiasi dato che protegga informazioni — password, token di accesso, chiavi crittografiche — va sempre usata l'API crittografica del browser. Crypto.getRandomValues() è disponibile in tutti i browser moderni senza librerie esterne e non introduce complessità aggiuntiva nel codice.
Validare sempre gli input
Prima di eseguire qualsiasi calcolo, è necessario validare i valori inseriti dall'utente. Se min è maggiore di max, la formula restituisce risultati errati. Se si chiedono più numeri unici di quanti ne contenga l'intervallo, il ciclo di generazione non termina mai, e un controllo preventivo risolve entrambi i casi con poche righe di codice.
if (min > max) throw new Error('min deve essere minore di max');
if (count > max - min + 1) throw new Error('count supera la dimensione dell\'intervallo');