← Voltar ao blog Projetos

Como Criei um Sorteador de Nomes Integrado ao Google Sheets

Tela inicial do Sorteador: Quem será o sortudo?

Sorteios parecem a coisa mais simples do mundo: você tem uma lista de nomes e escolhe um ao acaso. Mas, quando o sorteio acontece ao vivo, na frente de uma plateia, com os participantes vindo de um formulário de presença e várias palestras ao longo do dia, os detalhes começam a importar. Neste artigo, conto como construí o Sorteador, uma aplicação web feita só com HTML, CSS e JavaScript puro, que importa os participantes direto do Google Sheets e sorteia sem repetir ninguém.

O código é aberto e está no meu GitHub.

Tela do Sorteador com a lista de participantes, seleção de palestra e o painel de resultado
A tela principal: lista de participantes à esquerda e o painel de resultado à direita.

O problema

A necessidade era sortear participantes de palestras. Cada palestra tinha o seu próprio formulário do Google Forms, usado para registrar a presença, e as respostas caíam em uma planilha do Google Sheets. Na hora do sorteio, alguém precisaria abrir a planilha certa, copiar os nomes, colar em algum site de sorteio genérico e torcer para não ter nomes duplicados ou alguém sorteado duas vezes.

Eu queria uma ferramenta que resolvesse isso em poucos cliques: escolher a palestra, carregar os nomes e sortear, com uma apresentação bonita o suficiente para ser projetada em um telão.

Por que JavaScript puro, sem framework

Escolhi não usar React, Vue ou qualquer framework. O projeto inteiro são quatro arquivos: index.html, styles.css, script.js e um pequeno palestras.js de configuração. Os motivos:

  • Não precisa de servidor: tudo roda no navegador. A hospedagem é estática, na Vercel, sem custo.
  • Carrega instantaneamente: não há bundle de framework para baixar, o que importa quando a internet do evento é instável.
  • Manutenção simples: qualquer pessoa com conhecimento básico de web consegue entender e alterar o código.

Como funciona o sorteio

O coração da aplicação é a função de sorteio. Ela segue três regras: cada nome tem a mesma chance, ninguém é sorteado duas vezes no mesmo sorteio e é possível sortear vários nomes de uma vez.

Para garantir que ninguém repita, os nomes já sorteados ficam guardados em um Set. A cada rodada, a lista de disponíveis é filtrada, e os vencedores são retirados de uma cópia dessa lista com splice, o que impede que o mesmo nome saia duas vezes na mesma rodada:

const pool = [...availableNames];

for (let index = 0; index < quantity; index += 1) {
  const selectedIndex = Math.floor(Math.random() * pool.length);
  const [selectedName] = pool.splice(selectedIndex, 1);
  selectedNames.push(selectedName);
  drawnNames.add(normalizeName(selectedName));
}

Antes do resultado aparecer, há uma contagem regressiva de cinco segundos no próprio botão ("Sorteando em 5s"). Tecnicamente, ela não é necessária, mas faz toda a diferença na experiência ao vivo: cria expectativa na plateia e deixa claro para todos que o sorteio está acontecendo naquele momento.

Quando a quantidade é maior que um, os vencedores aparecem um por vez, com setas para navegar entre eles. Assim, cada nome ganha o seu momento na tela, em vez de uma lista despejada de uma só vez. E o botão "Reiniciar sorteio" libera todos os nomes novamente, se for preciso começar do zero.

Evitando nomes duplicados

Em formulários de presença, é comum a mesma pessoa responder duas vezes, ou digitar o nome com letras maiúsculas e minúsculas diferentes. Se nada fosse feito, essa pessoa teria o dobro de chances. A solução foi normalizar os nomes antes de comparar e usar um Map para manter só uma ocorrência de cada:

function normalizeName(name) {
  return name.trim().toLocaleLowerCase('pt-BR');
}

function getNames() {
  const uniqueNames = new Map();
  namesInput.value
    .split(/\r?\n/)
    .map((name) => name.trim())
    .filter(Boolean)
    .forEach((name) => uniqueNames.set(normalizeName(name), name));
  return [...uniqueNames.values()];
}

O uso de toLocaleLowerCase('pt-BR') em vez do toLowerCase() comum garante o tratamento correto de letras acentuadas em português.

Lendo o Google Sheets sem API e sem servidor

Essa foi a parte mais interessante do projeto. Eu não queria criar um backend nem configurar credenciais da API do Google só para ler uma planilha. A solução foi aproveitar um recurso pouco conhecido: uma planilha compartilhada como "qualquer pessoa com o link" pode ser baixada em formato CSV por um endereço específico.

A função buildCsvUrl recebe o link normal da planilha, aquele que você copia do navegador, e o transforma no endereço de exportação. Ela trata dois formatos:

  • Planilha compartilhada por link (/spreadsheets/d/ID/edit): é convertida para o endpoint /gviz/tq?tqx=out:csv, respeitando a aba (gid) do link.
  • Planilha publicada na web (/spreadsheets/d/e/ID/pubhtml): é convertida para /pub?output=csv.

Com o CSV em mãos, o próximo desafio foi interpretá-lo. Separar por vírgula parece fácil, até aparecer um nome como "Silva, João" entre aspas, ou um campo com aspas dentro. Por isso, escrevi um pequeno parser que percorre o texto caractere por caractere e controla se está dentro ou fora de aspas, tratando também aspas duplicadas e quebras de linha do Windows e do Linux.

Outro detalhe: o Google Forms sempre coloca o "Carimbo de data/hora" na primeira coluna. O código procura automaticamente uma coluna cujo título contenha "nome" ou "name" e, se não encontrar, usa a segunda coluna. Assim, funciona com a maioria dos formulários sem nenhuma configuração.

Quando a planilha não está compartilhada corretamente, o Google devolve uma página HTML de login em vez do CSV. O código detecta isso verificando se a resposta começa com < e mostra uma mensagem clara pedindo para compartilhar a planilha como "qualquer pessoa com o link", em vez de um erro genérico.

Uma planilha para organizar todas as palestras

A primeira versão da seleção de palestras tinha um formulário dentro do próprio sorteador para cadastrar dia, horário e link de cada palestra, salvando tudo no localStorage do navegador. Funcionou, mas logo apareceu o problema: os dados ficavam presos naquele computador. Se o sorteio fosse feito em outra máquina, era preciso cadastrar tudo de novo.

Ainda no mesmo dia, troquei essa abordagem por algo mais simples para quem organiza: uma planilha central do Google Sheets com as colunas Dia, Hora, Título e Link. O endereço dela fica no arquivo palestras.js, e o sorteador lê essa planilha ao abrir. Qualquer pessoa da organização pode atualizar a programação pela própria planilha, sem tocar no código.

Para facilitar ainda mais, ao abrir a página o sistema já seleciona o dia de hoje e a palestra que está acontecendo naquele horário. Na maioria das vezes, basta clicar em "Carregar" e depois em "Sortear". As datas e horários são normalizados, então tanto "02/10" quanto "2026-10-02", ou "8h" e "08:00", são aceitos.

Também deixei uma opção "Usar outro link", para importar qualquer planilha de respostas manualmente, e um botão "Atualizar", que busca só os nomes novos de uma palestra já carregada. Isso é útil quando pessoas ainda estão respondendo o formulário minutos antes do sorteio.

Sorteio de números

A última funcionalidade adicionada foi o sorteio de números: você informa um intervalo, como de 1 a 100, e a lista é preenchida automaticamente. Serve para rifas, números de inscrição ou qualquer situação em que os participantes são identificados por número. Para evitar travar o navegador, se o intervalo passar de 100 mil números, a aplicação pede confirmação antes de gerar a lista.

Detalhes de interface que fazem diferença

  • Contador de nomes sempre visível, atualizado conforme você digita ou importa.
  • Mensagens de status com aria-live, para que leitores de tela anunciem o resultado do sorteio.
  • Validações amigáveis: se você pedir para sortear mais nomes do que há disponíveis, a mensagem diz exatamente quantos restam.
  • Visual pensado para projeção: o nome sorteado aparece grande, em um painel escuro com alto contraste, legível do fundo da sala.

O que aprendi

  • Nem todo projeto precisa de framework ou backend. Para uma ferramenta focada, JavaScript puro e hospedagem estática deixam tudo mais rápido e mais barato.
  • Os dados devem ficar onde as pessoas já trabalham. Trocar o cadastro no localStorage pela planilha foi a mudança que tornou a ferramenta realmente prática para a organização.
  • Os casos extremos aparecem no uso real: nomes duplicados, vírgulas dentro de nomes, planilhas sem permissão. Tratar cada um com uma mensagem clara evita constrangimento na frente da plateia.
  • Experiência também é funcionalidade. A contagem regressiva não muda o resultado, mas muda completamente a sensação do sorteio.

O que ainda pode melhorar

Algumas ideias para as próximas versões: usar crypto.getRandomValues no lugar do Math.random, que é uma fonte de aleatoriedade mais robusta; manter um histórico dos sorteios realizados, com data e hora, para transparência; e um modo de tela cheia dedicado à projeção.

Experimente

O Sorteador está publicado e pode ser usado gratuitamente em sorteador-swart.vercel.app. Basta colar os nomes, um por linha, e clicar em "Sortear nome". Se quiser adaptar para o seu evento, o código completo está no repositório do GitHub.