OnKey
Ti mostra in tempo reale dove stoni mentre canti
Cosa fa
App nata da un creatore che si definisce un cattivo cantante: mentre canti, mostra la nota che stai facendo e quella giusta, così vedi dove sei stonato.
Come guadagna
Presentato su Hacker News dal suo creatore, che non dichiara incassi. Idee nostre per guadagnare con un progetto simile: versione gratuita con poche canzoni e abbonamento per il catalogo completo; pacchetti per scuole di canto e cori.
Rendilo tuo
- Per i cori parrocchiali e le scuole di musica italiane, con i brani del loro repertorio.
- Sfide di karaoke tra amici con punteggio di intonazione.
Le frasi da scrivere all'AI
1. Base del sito e grafica
Crea un'app web in italiano chiamata "Intonato": mentre canti, mostra in tempo reale la nota che stai facendo e quella giusta, così vedi subito dove sei stonato; con esercizi di intonazione per migliorare. Usa React, Vite, TypeScript e Tailwind CSS.
Pagine da creare:
1. Pagina iniziale con una demo dal vivo e il prezzo
2. Accordatore della voce: nota cantata in grande, con l'ago che mostra se sei alto o basso
3. Esercizi: scale, intervalli e melodie da seguire
4. Risultato dell'esercizio con il grafico della voce sulla linea giusta
5. Progressi: precisione nel tempo
Stile: musicale e colorato: sfondo blu notte #0F172A, ciano #22D3EE per la voce, verde #22C55E quando sei intonato, rosso #F43F5E quando sei stonato, carattere "Poppins". Nessuna immagine generica di stock.
Tutto deve funzionare perfettamente da telefono. Tutti i testi in italiano, chiari e senza parole tecniche.
2. Database, account e sicurezza
Collega il progetto a Supabase e attiva la registrazione con email e password.
Crea queste tabelle:
- "esercizi_fatti": esercizio, precisione in percentuale, scostamento medio in centesimi di semitono, data, collegati all'utente
Regole di sicurezza obbligatorie:
- ogni utente vede solo i suoi risultati
- l'audio del microfono non viene mai registrato né inviato: si analizza nel browser
- il piano non si modifica dal browser
Usa esattamente il codice SQL che trovi nei "Dettagli tecnici" di questa scheda: eseguilo nel SQL Editor di Supabase prima di continuare, poi collega le pagine alle tabelle.
3. Riconoscere la nota in tempo reale
L'app ascolta il microfono con il Web Audio API e stima la frequenza della voce con il metodo dell'autocorrelazione (codice nei Dettagli tecnici), circa 30 volte al secondo. Converte la frequenza nella nota (es. La4) e nello scostamento in centesimi: l'ago va a sinistra se sei basso, a destra se sei alto, e diventa verde entro ±15 centesimi.
L'audio non viene registrato né inviato: tutto succede nel browser. Avviso per usare le cuffie negli esercizi con base, così il microfono non sente la musica.
4. Esercizi e progressi
Esercizi: suona una nota di riferimento e chiede di cantarla; scale maggiori; intervalli; brevi melodie note da seguire su una linea che scorre. Durante l'esercizio si vede la linea della propria voce sovrapposta a quella giusta.
Risultato con la precisione in percentuale e i punti dove si è più spesso stonati. Grafico dei progressi settimana per settimana.
5. Pagamenti con Stripe
Aggiungi un abbonamento a 3,99 € al mese. Gratis l'utente ha: l'accordatore della voce e 3 esercizi. Con il piano Pro sblocca: tutti gli esercizi, melodie, estensione vocale e progressi.
A pagamento riuscito il webhook porta l'utente al piano Pro; se l'abbonamento viene disdetto o non rinnovato lo riporta al piano gratuito.
Per la sessione di pagamento e il webhook segui la guida "Pagamenti con Stripe" di Fattibile.
6. Controlli specifici di questo progetto
Oltre ai controlli della guida "Controllo finale", verifica in particolare:
1. Precisione: fai suonare a un'app accordatore un La a 440 Hz vicino al microfono: Intonato deve mostrare La4 con scostamento vicino a zero.
Correggi quello che non va e scrivimi cosa hai cambiato.
Dettagli tecnici
Dalla voce alla nota
Stima la frequenza fondamentale con l'autocorrelazione (confronta il suono con se stesso spostato nel tempo) e la converte in nota e centesimi.
const NOTE = ["Do", "Do#", "Re", "Re#", "Mi", "Fa", "Fa#", "Sol", "Sol#", "La", "La#", "Si"];
// Frequenza in Hz di un pezzo di audio, o null se è silenzio o rumore
export function frequenza(campioni: Float32Array, frequenzaCampionamento: number): number | null {
const volume = Math.sqrt(campioni.reduce((s, x) => s + x * x, 0) / campioni.length);
if (volume < 0.01) return null; // troppo silenzioso
const minRitardo = Math.floor(frequenzaCampionamento / 1000); // 1000 Hz
const maxRitardo = Math.floor(frequenzaCampionamento / 70); // 70 Hz
let migliore = -1, valoreMigliore = 0;
for (let r = minRitardo; r <= maxRitardo; r++) {
let somma = 0;
for (let i = 0; i < campioni.length - r; i++) somma += campioni[i] * campioni[i + r];
if (somma > valoreMigliore) { valoreMigliore = somma; migliore = r; }
}
return migliore > 0 ? frequenzaCampionamento / migliore : null;
}
// 440 Hz -> { nota: "La4", centesimi: 0 }
export function nota(hz: number) {
const semitoni = 12 * Math.log2(hz / 440) + 69; // numero MIDI
const vicina = Math.round(semitoni);
return {
nota: NOTE[vicina % 12] + (Math.floor(vicina / 12) - 1),
centesimi: Math.round((semitoni - vicina) * 100), // da -50 a +50
};
}
export async function ascolta(onNota: (n: ReturnType<typeof nota>) => void) {
const audio = new AudioContext();
const microfono = audio.createMediaStreamSource(await navigator.mediaDevices.getUserMedia({ audio: true }));
const analizzatore = audio.createAnalyser();
analizzatore.fftSize = 2048;
microfono.connect(analizzatore);
const dati = new Float32Array(analizzatore.fftSize);
setInterval(() => {
analizzatore.getFloatTimeDomainData(dati);
const hz = frequenza(dati, audio.sampleRate);
if (hz) onNota(nota(hz));
}, 33);
}Risultati degli esercizi
Ogni esercizio fatto con la precisione, per il grafico dei progressi.
create table esercizi_fatti (
id uuid primary key default gen_random_uuid(),
utente_id uuid not null references auth.users(id) on delete cascade,
esercizio text not null,
precisione int not null check (precisione between 0 and 100),
scostamento_medio int not null, -- in centesimi di semitono
fatto_il timestamptz not null default now()
);
alter table esercizi_fatti enable row level security;
create policy "le mie righe" on esercizi_fatti
for all using (auth.uid() = utente_id) with check (auth.uid() = utente_id);
-- Precisione media per settimana
select date_trunc('week', fatto_il)::date as settimana, round(avg(precisione)) as precisione
from esercizi_fatti
where utente_id = auth.uid()
group by 1 order by 1;
Pezzi comuni a tutti i progetti
Glossario
- Centesimo (di semitono)
- Un centesimo della distanza tra due note vicine: sotto i 15 centesimi l'orecchio quasi non sente la differenza.
- Web Audio API
- La funzione del browser che analizza e crea suoni direttamente dal codice.