← Tutte le idee Giochi e hobby

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.