Bingo Pedagógico: um jogo de estimulação cognitiva para idosos feito com Next.js

Rafik Moreira -
22 de abril de 2026

Bingo Pedagógico

O projeto

A Tathiane Galvão (psicóloga clínica, CRP 01/27841) me procurou com uma ideia que achei muito legal: um bingo visual voltado pra idosos, pensado como ferramenta de estimulação cognitiva em sessões terapêuticas. É o bingo clássico, só que com imagens no lugar dos números, rodando direto no navegador sem precisar de cadastro nem servidor.

Parecia simples: gerar cartelas, sortear imagens, imprimir. Só que conforme fui conversando com ela e entendendo melhor o contexto, vi que tinha bastante coisa pra considerar. Os idosos muitas vezes têm dificuldade visual ou motora. Os psicólogos e cuidadores que mediam o jogo nem sempre manjam de tecnologia. E os lugares onde isso roda (clínicas, centros de convivência) nem sempre têm internet boa.

Essas restrições moldaram praticamente todas as decisões técnicas do projeto.

O problema real

A Tathiane já usava bingo nas sessões, mas fazia tudo na mão: recortava imagens, colava em cartolina, sorteava de um saquinho. Dava certo, mas dava trabalho. E quando ela queria mudar o tema do bingo pra algo mais personalizado (tipo fotos da família do paciente), tinha que refazer tudo do zero.

Ela precisava de quatro coisas:

  1. Gerar cartelas automaticamente com imagens, sem repetição entre elas
  2. Sortear imagens na tela pra todo mundo ver, com o nome embaixo (muitos participantes não leem, mas a legenda ajuda quem consegue)
  3. Imprimir as cartelas em A4, prontas pra recortar e distribuir
  4. Montar bingos com imagens próprias, sem depender de internet

Por que não tem backend

A decisão foi: nada de banco de dados, autenticação ou servidor. Parece limitante, mas faz sentido quando você pensa em quem vai usar.

Psicólogo não quer criar conta. Não quer depender de conexão. Quer abrir o navegador, montar o jogo e começar. E se a internet cair no meio da sessão, o sorteio não pode travar.

Por isso toda a persistência fica no próprio navegador. Dados leves como cartelas geradas e estado do sorteio usam localStorage. Já as imagens dos bingos customizados ficam no IndexedDB, que não tem a limitação de 5 MB do localStorage e aguenta tranquilamente dezenas de imagens em base64. Depois que a página carrega, funciona offline sem problema.

export function saveGeneratedCards(key: string, cards: BingoCard[]) {
  localStorage.setItem(key, JSON.stringify(cards));
}

export function getGeneratedCards(key: string): BingoCard[] {
  const data = localStorage.getItem(key);
  return data ? JSON.parse(data) : [];
}

Pra imagens, o IndexedDB resolve o problema de espaço:

const db = await openDB('bingo-store', 1, {
  upgrade(db) {
    db.createObjectStore('custom-bingos');
  },
});

await db.put('custom-bingos', bingoData, bingoId);
const bingo = await db.get('custom-bingos', bingoId);

O lado ruim é que se trocar de computador, perde tudo. Mas esses profissionais costumam usar sempre a mesma máquina no consultório. A simplicidade compensa.

Dois modos de jogo

Bingo Padrão

Já vem com 16 imagens pré-definidas divididas em quatro categorias: emoções, autocuidado, socialização e geral. As cartelas 3×3 não são montadas de forma puramente aleatória. O gerador distribui imagens de cada categoria proporcionalmente pra garantir variedade.

Bingo Customizado

Aqui o usuário sobe as próprias imagens (PNG, JPG ou WebP, até 5MB cada), dá um nome pro tema e tá pronto. As imagens viram base64 e ficam guardadas no IndexedDB do navegador. A primeira versão usava localStorage pra tudo, mas o limite de 5 MB estourava rápido com poucas imagens. O IndexedDB não tem essa restrição e funciona offline do mesmo jeito, sem precisar de storage externo nenhum.

Precisa de no mínimo 9 imagens (o tamanho de uma cartela 3×3), mas o ideal são 16 pra ter variedade.

Garantindo cartelas únicas

Se o gerador de cartelas fosse aleatório puro, com poucas imagens disponíveis, algumas cartelas poderiam sair idênticas. Com 16 imagens escolhendo 9, C(16,9) dá 11.440 combinações possíveis, mas sem validação, nada impede repetição.

A solução foi calcular o número máximo de cartelas únicas pro conjunto de imagens e validar cada cartela gerada contra as anteriores:

function combination(n: number, k: number): number {
  if (k > n || k < 0) return 0;
  if (k === 0 || k === n) return 1;
  if (k > n - k) k = n - k;
  let result = 1;
  for (let i = 0; i < k; i++) {
    result = (result * (n - i)) / (i + 1);
  }
  return Math.floor(result);
}

export function getMaxUniqueCards(availableImages: BingoImage[]): number {
  return combination(availableImages.length, CARD_SIZE);
}

A interface mostra o limite: "Máximo de X cartelas únicas com Y imagens." Com 9 imagens dá pra gerar 1 cartela. Com 10, dá 10. Com 16, dá 11.440. A conta resolve o problema de UX.

O sorteio

Pensei o sorteio pra funcionar com projetor ou TV. Quando o mediador clica em "Sortear", a imagem aparece em tela cheia, bem grande, pensando em quem tem dificuldade pra enxergar. Toca em qualquer lugar da tela e volta pro painel de controle.

Se a página recarregar ou o navegador fechar sem querer, o jogo continua de onde parou. O estado do sorteio fica no localStorage (é leve, só IDs de imagens já sorteadas).

const { currentImage, drawnImages, remainingCount, canDraw, draw, reset } =
  useLottery(images, "bingo-standard-lottery");

Tem também um painel de mediação sugerida do lado direito, com perguntas que o psicólogo pode usar durante o jogo: "Como você se sente ao ver esta imagem?", "Te lembra de alguma coisa?". A Tathiane pediu isso porque nem todo profissional que aplica o bingo tem experiência com dinâmicas assim.

Impressão

Nesse projeto a impressão é tão importante quanto o sorteio. Os idosos jogam com cartelas de papel e isso é de propósito: faz parte da experiência tátil, da familiaridade com o jogo de verdade.

As cartelas impressas têm borda pontilhada (linha de recorte) e quinas retas pra facilitar o corte com tesoura. Footer e navegação somem na hora de imprimir. Tudo formatado pra A4.

@media print {
  @page {
    size: A4;
    margin: 1cm;
  }
}

Na tela a cartela tem sombra e bordas arredondadas; na impressão fica chapada com borda tracejada. A prop printMode do componente cuida dessa troca.

Stack

Next.js 16, React 19, Tailwind CSS v4, Vitest, Cypress, Biome, lucide-react, shadcn/ui.

Onde acessar

O projeto tá em bingo.rafikmoreira.dev.br. Se você trabalha com idosos ou conhece alguém que possa usar, compartilha. É livre.