Game Dev 15 de agosto de 2026• 4 min de leitura• Rafael Guimarães

GGColors por dentro: sons sintetizados com Web Audio e um jogo de memória acessível

Como o GGColors gera todos os seus sons em tempo real com a Web Audio API, organiza a partida numa máquina de estados simples e continua jogável só com teclado, leitor de tela ou sem som.

GGColors por dentro: sons sintetizados com Web Audio e um jogo de memória acessível

O GGColors é um jogo de memória de sequências, da mesma família do clássico Genius (Simon, fora do Brasil). O jogo acende uma cor, depois duas, depois três, e você precisa repetir a sequência inteira na ordem certa. Errou uma cor, a partida acaba.

A regra cabe numa frase, mas o jogo tem três decisões técnicas que valem um artigo: não existe nenhum arquivo de áudio, toda a partida é controlada por uma máquina de estados pequena, e o jogo foi pensado para ser jogável sem mouse, sem visão e sem som. Este texto mostra como cada uma delas funciona no código que roda hoje no portal.

Jogue o GGColors aqui no portal antes de ler, porque os detalhes ficam mais claros depois de uma ou duas partidas.

Como uma partida funciona

Cada rodada é uma fase. Na fase 1 a sequência tem uma cor; ao acertar, o jogo acrescenta uma nova cor aleatória no fim da sequência e mostra tudo de novo desde o começo. A sequência só cresce, e é isso que torna o jogo difícil: na fase 10 você precisa lembrar dez cores em ordem.

  • Cada cor da sequência acende por 400 ms, e uma nova cor começa a cada 1 segundo.
  • Depois da última cor, o jogo espera 600 ms e libera a sua vez.
  • Acertar a sequência toca um pequeno arpejo de vitória e, 1,5 s depois, começa a fase seguinte.
  • Errar qualquer cor encerra a partida com a mensagem "Game Over" e informa em qual posição você errou ("Errou na cor 4 de 7").

Esses tempos parecem detalhe, mas definem a dificuldade. Um segundo por cor é lento o bastante para uma criança acompanhar e, ainda assim, fica exigente quando a sequência passa de oito ou nove cores.

Uma máquina de estados de cinco estados

Toda a lógica da partida vive numa classe com cinco estados possíveis:

EstadoO que está acontecendoO jogador pode clicar?
idleAntes de começarNão
showingO jogo está exibindo a sequênciaNão
waitingVez do jogadorSim
successSequência completa, próxima fase a caminhoNão
errorCor errada, fim de partidaNão

A regra mais importante do jogo está numa linha só: a entrada do jogador é ignorada em qualquer estado que não seja waiting.

handleInput(color) {
  if (this.state !== 'waiting') {
    return { success: false, complete: false, message: '' };
  }
  this.userInput.push(color);
  const index = this.userInput.length - 1;

  if (color !== this.sequence[index]) return this.handleError(index);
  if (this.userInput.length === this.sequence.length) return this.handlePhaseComplete();
  return { success: true, complete: false,
           message: `Correta! Continue... ${this.userInput.length}/${this.sequence.length}` };
}

Sem esse bloqueio, um clique apressado durante a exibição da sequência contaria como resposta, e o jogador perderia sem ter errado nada. Comparar a cor digitada com sequence[index] a cada clique, em vez de esperar a sequência inteira, também permite dar o retorno na hora e dizer exatamente onde foi o erro.

O problema dos timeouts órfãos

A exibição da sequência é uma cadeia de setTimeout: acender a cor, apagar, acender a próxima, liberar a vez do jogador. Se o jogador reinicia a partida no meio dessa cadeia, os timeouts antigos continuam agendados e, segundos depois, acendem cores de uma partida que já não existe.

A solução foi registrar todo timeout numa lista e cancelar a lista inteira sempre que a partida recomeça:

scheduleTimeout(fn, delay) {
  const id = setTimeout(() => {
    this.timeouts = this.timeouts.filter(t => t !== id);
    fn();
  }, delay);
  this.timeouts.push(id);
}

clearTimeouts() {
  this.timeouts.forEach(clearTimeout);
  this.timeouts = [];
}

start(), reset() e o tratamento de erro chamam clearTimeouts() antes de qualquer outra coisa. É um padrão pequeno que vale para qualquer jogo baseado em temporizadores: se você agenda, você precisa conseguir desagendar.

Todo som é gerado na hora, com Web Audio

O GGColors não carrega nenhum MP3 ou WAV. Cada nota é criada no momento em que toca, com a Web Audio API do navegador: um oscilador gera a onda e um nó de ganho controla o volume.

function playTone(frequency, duration, type = 'sine', volume = 0.3) {
  const ctx = getAudioContext();
  if (!ctx) return; // navegador sem Web Audio: o jogo segue sem som

  const osc = ctx.createOscillator();
  const gain = ctx.createGain();
  osc.connect(gain);
  gain.connect(ctx.destination);

  osc.frequency.value = frequency;
  osc.type = type;

  const now = ctx.currentTime;
  gain.gain.setValueAtTime(0, now);
  gain.gain.linearRampToValueAtTime(volume, now + 0.02);           // ataque: 20 ms
  gain.gain.setValueAtTime(volume, now + duration - 0.05);
  gain.gain.exponentialRampToValueAtTime(0.01, now + duration);    // saída: 50 ms

  osc.start(now);
  osc.stop(now + duration);
}

Três vantagens aparecem na prática:

  1. Carregamento instantâneo. Não há arquivos de áudio para baixar antes de jogar.
  2. Nenhum estalo. Ligar e desligar um oscilador de uma vez produz um "clique" audível. A rampa de 20 ms na entrada e a queda exponencial nos últimos 50 ms eliminam esse ruído. A queda vai até 0.01, e não até zero, porque exponentialRampToValueAtTime não aceita zero como alvo.
  3. Falha elegante. Se o navegador não oferece Web Audio, a função retorna sem fazer nada e o jogo continua funcionando, só que em silêncio.

As notas de cada cor

Cada cor tem uma frequência fixa, tocada numa onda senoidal (o timbre mais "limpo" que o oscilador oferece):

CorFrequênciaTeclas
Vermelho396 Hz1 ou Q
Verde528 Hz2 ou W
Azul639 Hz3 ou E
Amarelo417 Hz4 ou R

Esses valores vêm de uma série de frequências conhecida como Solfeggio, escolhida aqui por um motivo musical: são quatro notas bem separadas entre si, fáceis de distinguir de ouvido, o que ajuda quem memoriza a sequência pelo som e não pela cor. A série costuma ser associada a efeitos terapêuticos, mas não há evidência científica desses efeitos, e o GGColors não faz nenhuma promessa desse tipo. No jogo, elas são apenas quatro notas agradáveis que não se confundem.

Os outros sons seguem a mesma ideia. O início da partida toca as quatro notas em sequência (396 → 417 → 528 → 639 Hz); acertar uma fase toca um arpejo ascendente (528 → 639 → 741 Hz); errar toca uma nota grave de 174 Hz, bem abaixo das notas das cores, para soar claramente como "fim".

Jogável sem mouse, sem visão e sem som

Um jogo de memória de cores parece, à primeira vista, impossível para quem não enxerga as cores ou não ouve os sons. O GGColors resolve isso oferecendo três canais redundantes, visual, sonoro e textual, para a mesma informação.

Teclado

Todas as ações têm atalho, e os atalhos ficam visíveis na tela:

  • 1 a 4 ou Q, W, E, R para as cores;
  • Espaço ou Enter para começar;
  • Escape para reiniciar.

Os dois conjuntos de teclas existem por conforto: números para quem joga com o teclado numérico, QWER para quem prefere deixar a mão esquerda parada na posição de jogo.

Leitor de tela

As mensagens do jogo ("Correta! Continue... 3/5", "Fase 4 completada!", "Game Over") ficam numa região com aria-live="polite" e aria-atomic="true". Na prática, o leitor de tela anuncia cada mensagem nova assim que ela aparece, sem interromper o que já estava lendo, e sempre lê a mensagem inteira, e não só o trecho que mudou.

Sem som

Cada nota é acompanhada de um indicador visual que pulsa junto com o som. Quem joga sem áudio, por escolha ou por deficiência auditiva, recebe a mesma informação pelos olhos.

Esses recursos seguem as diretrizes WCAG 2.1, principalmente as que pedem que toda funcionalidade esteja disponível pelo teclado e que nenhuma informação dependa de um único sentido.

O que dá para reaproveitar em outros jogos

  • Bloqueie a entrada fora da vez do jogador com um estado explícito, em vez de flags espalhadas.
  • Registre e cancele todos os timeouts; timeouts órfãos são uma das fontes mais comuns de bugs em jogos simples.
  • Sintetize sons curtos com Web Audio: sem arquivos, sem espera e sem estalos, se você usar rampas de volume.
  • Duplique toda informação importante em pelo menos dois canais (cor + som + texto).

O GGColors é pequeno, e justamente por isso é um bom lugar para ver essas ideias funcionando juntas. Jogue uma partida e tente passar da fase 10. Para ver outra forma de animar cartas e peças com CSS, leia também Virando cartas com CSS 3D.

RG

Rafael Guimarães

Criador do Guima Games Portal & Game Developer

Ver mais artigos