Timelapse 3D · 100% local

A história da cidade, crescendo no mapa

Do primeiro povoado até hoje: a cidade nasce e cresce em 3D, com ano, população, bandeira de cada época e cartões com fonte. Sem IA de vídeo e sem custo de render.

Banner: História em 3D
Em resumo

O historia-timelapse é um conjunto de scripts que transforma a história de uma cidade num vídeo em 3D, em timelapse. Ele usa o relevo real do terreno e os prédios do OpenStreetMap: cada prédio "nasce" no ano em que o bairro dele foi ocupado, e a cidade cresce na tela junto com o contador de população do IBGE. Os acontecimentos aparecem em cartões com número, explicação e fonte. Para usar, você precisa de um computador com placa de vídeo, Python e Node, e de uma pesquisa histórica da cidade.

Vídeos

Os vídeos

Feitos com este motor, no canal Aventuras INEMA. Cada cidade tem versão sem narração e narrada.

Florianópolis — de Desterro à capital

6 min: sambaquis, Dias Velho, fortalezas, açorianos, a invasão espanhola de 1777, as pontes e os aterros. Narrada: aqui. Abrir no YouTube

Rio de Janeiro — de 1500 à metrópole

7 min: tupinambás, França Antártica, a Corte em 1808, Império e República. Narrada: aqui. Abrir no YouTube

Itaguaí — da aldeia ao porto

6 min: da aldeia jesuíta à fazenda de Santa Cruz e ao porto. Narrada: aqui. Abrir no YouTube

Centro de Itaguaí — as ruas, de perto

5 min, com a câmera nas ruas e nos prédios históricos. Narrada: aqui. Abrir no YouTube

Porto Alegre com luz real (Mitsuba)

1min35, narrado: o mesmo Porto Alegre com relevo e prédios iluminados por path tracing na GPU — sombras de verdade e prédios subindo no ano do bairro. Abrir no YouTube

Porto Alegre — 1772 a 2024

1min35, narrado (o piloto): fundação, cercos farroupilhas, a cheia de 1941, o Muro da Mauá e a cheia de 2024. Abrir no YouTube

O que é

Um motor de história em mapa 3D

Dados reais em vez de imagens inventadas.

Banner com os recursos do historia-timelapse

🏙️ A cidade nasce ano a ano

Relevo Copernicus de 30 m e prédios do OpenStreetMap (com a base aberta da Microsoft onde o OSM tem pouco). Cada prédio ganha um ano pelo bairro e pela curva de população.

📜 Cartões com fonte

Cada evento tem título, número grande, explicação e fonte. Ano, população (censos do IBGE) e bandeira do regime mudam no canto da tela.

🎬 Efeitos de história

Globo com zoom até a cidade, linha do tempo, frotas navegando, cercos, fortalezas, pontes desenhadas, aterros crescendo em cor e cheias subindo.

Como funciona

Do dado ao vídeo

Tudo roda na sua máquina: deck.gl no Chrome com a GPU, cerca de 0,1 s por quadro em 1080p.

Pesquisa histórica→ OSM + relevo→ Ano de cada prédio→ Roteiro (blocos)→ Cena→ Render + trilha
Abertura com o globo
Abertura: globo com zoom até a cidade
Fortaleza de Anhatomirim em destaque
Fortaleza de Anhatomirim (1739) em destaque
Florianópolis em 1961
Florianópolis em 1961: nasce a UFSC
Porto Alegre na cheia de 2024
Porto Alegre, maio de 2024: a água volta ao Centro
Pré-requisitos

O que precisa

Linux com placa de vídeo, Python 3, Node 20+ e ffmpeg. Os dados são arquivos públicos baixados por um script; não há API nem custo.

Programas

Python, Node, ffmpeg e git.

sudo apt install python3-venv ffmpeg git
# Node 20+: via nvm (nvm install 20) ou NodeSource — o nodejs do apt é antigo

Placa de vídeo

O render usa o Chrome com a GPU (Vulkan). Sem Vulkan, rode com GL="--use-angle=gl" antes do comando (mais lento).

# ~0,1 s por quadro em 1080p numa GPU NVIDIA

Espaço em disco

O recorte regional do OSM tem centenas de MB (o Sul do Brasil, ~430 MB). Cada bloco de relevo tem 20–50 MB.

# tudo fica na pasta dl/ (fora do git)
Guia de uso · passo a passo

Faça o vídeo da sua cidade

Cada cidade mora em cidades/<id>/. Os scripts leem a cidade pela variável CIDADE. Os exemplos usam Florianópolis (floripa): troque pelo id da sua cidade em todos os comandos e mantenha export CIDADE=<id> no terminal (sem ele os scripts param com aviso).

0

Baixe o projeto e instale

Cria o ambiente Python (venv = uma pasta com as bibliotecas só deste projeto) e instala o Playwright, que abre o Chrome para gravar os quadros.

git clone https://github.com/inematds/historia-timelapse && cd historia-timelapse
python3 -m venv .venv
.venv/bin/pip install osmium tifffile imagecodecs \
    shapely numpy pyproj scipy pillow
cd motor && npm i && npx playwright install --with-deps chromium && cd ..
1

Crie a configuração da cidade

bbox é o retângulo do mapa em graus: oeste, sul, leste, norte (dá para desenhar em bboxfinder.com). municipio_osm é o nome exato do município no OpenStreetMap. centro_antigo é o ponto onde a cidade começou.

# cidades/floripa/config.json
{ "nome": "Florianópolis", "bbox": [-48.80, -27.92, -48.33, -27.33],
  "pbf": "dl/sul.osm.pbf",
  "pbf_url": "https://download.geofabrik.de/south-america/brazil/sul-latest.osm.pbf",
  "municipio_osm": "Florianópolis", "centro_antigo": [-48.5490, -27.5965],
  "pais_microsoft": "Brazil" }
# opcionais: "efeitos" (pontes, fortes, aterros), "tex" (tamanho da textura),
# "agua_baixa_m", "pop_modelo_extra" — veja cidades/floripa/config.json
2

Baixe os dados públicos

O script baixa o recorte do OSM (formato .pbf), os blocos de relevo que cobrem o seu bbox e, com --microsoft, os prédios da Microsoft para onde o OSM tem pouco prédio.

export CIDADE=floripa
.venv/bin/python -I scripts/baixar_dados.py --microsoft
3

Faça a pesquisa histórica

Em cidades/<id>/dados/pesquisa/ vão três arquivos. Copie os de Florianópolis como modelo e troque o conteúdo.

# populacao.json — censos e estimativas
{ "serie": [ { "ano": 1872, "pop": 25709, "fonte_url": "https://..." } ] }
# eventos.json — um evento por item, com coordenada e fonte
{ "eventos": [ { "id": "ponte-hercilio", "ano": 1926, "titulo_curto_PT": "A PONTE HERCÍLIO LUZ",
    "legenda": "...", "detalhe": "...", "numero_impacto": "821 METROS",
    "local": { "lat": -27.5935, "lon": -48.5649 }, "fontes": ["https://..."] } ] }
# bairros_epocas.json — ano em que cada bairro passou a ser ocupado
{ "bairros": [ { "bairro": "Trindade", "ano_aprox_ocupacao": 1960 } ] }
4

Prepare o mapa

Recorta prédios, água e bairros, monta o relevo com o mar, dá a cada prédio um ano de nascimento e prepara pontes, fortes e aterros.

.venv/bin/python -I scripts/extrai_osm.py   # usa o "pbf" do config
.venv/bin/python -I scripts/extrai_msbuild.py   # só se baixou os prédios da Microsoft
.venv/bin/python -I scripts/prep_terreno.py
.venv/bin/python -I scripts/prep_predios.py
.venv/bin/python -I scripts/modelo_anos.py
.venv/bin/python -I scripts/extrai_feicoes.py
.venv/bin/python -I scripts/prep_efeitos.py
5

Escreva o roteiro e gere a cena

O roteiro.json é um objeto com a lista "blocos" (e, se usar, "linhas", "fortes", "areas" e "regimes"). Para uma cidade nova você escreve esse arquivo à mão, ou copia e adapta cidades/floripa/roteiro_floripa.py, que o monta a partir do eventos.json. Cada bloco tem os anos (início, ano do evento, fim), a câmera de ida e de chegada, a duração e o cartão. Em Florianópolis ele é montado por roteiro_floripa.py a partir da pesquisa; use como modelo.

# um bloco do roteiro.json
{ "id": "ponte-hercilio", "anos": [1895, 1926, 1927], "pulo": 2.0, "dur": 9.5,
  "cam": [ {"lon": -48.552, "lat": -27.600, "zoom": 14, "pitch": 58, "bearing": -40},
           {"lon": -48.570, "lat": -27.602, "zoom": 13.7, "pitch": 60, "bearing": -70} ],
  "cartao": { "numero": "821 METROS", "titulo": "A ponte Hercílio Luz",
              "legenda": "...", "detalhe": "...", "fonte": "IBGE" },
  "destaca": ["linha:hercilio"] }
# anos: [início, ano do evento, fim]; "pulo": segundos em que o tempo corre até o ano do evento
# cam: lon/lat do centro, zoom (12 = bairro, 14 = quadra), pitch (inclinação 0–60), bearing (rumo em graus)
# "destaca": "linha:<id>" | "forte:<id>" | "area:<id>" — ids definidos em "efeitos" no config e listados no topo do roteiro
# ajuste a câmera olhando os quadros do passo 6
python3 cidades/floripa/roteiro_floripa.py
.venv/bin/python -I scripts/gerar_cena.py   # gera cidades/floripa/cena/cena.js
6

Confira alguns quadros

Grava só os segundos pedidos, para olhar enquadramento, cartões e efeitos antes do vídeo inteiro.

cd motor && node render.mjs ../cidades/floripa/cena --quadros 20,60,120 && cd ..
# as imagens q_*.jpg ficam em cidades/floripa/cena/
7

Renderize, ponha a música e junte

O render é feito em trechos de 60 s (o navegador reinicia entre eles, para não faltar memória). Ponha músicas instrumentais suas (.mp3) em cidades/floripa/musica/; elas tocam em ordem alfabética, em laço.

bash motor/render_trechos.sh cidades/floripa/cena cidades/floripa/final/video.mp4
.venv/bin/python -I scripts/montar_musica.py   # cidades/floripa/final/audio.wav
ffmpeg -i cidades/floripa/final/video.mp4 -i cidades/floripa/final/audio.wav \
  -map 0:v -map 1:a -c:v copy -c:a aac -shortest cidades/floripa/final/floripa.mp4
8

Versão narrada (opcional)

A narração é uma camada de áudio sobre o mesmo vídeo: um texto por bloco em cidades/<id>/narracao.json, encaixado no início de cada evento. A gravação usa um motor de voz local do autor (cardshorts, fora deste repositório); com outra voz, grave um arquivo por bloco e preencha narracao/tempos.json.

# narracao/tempos.json, se gravar com outra voz: {"<bloco>": {"dur": 6.2, "wav": "caminho.wav", "texto": "...", "ouvido": "..."}}
python3 scripts/narrar.py          # só com o cardshorts; com outra voz, pule e preencha o tempos.json
python3 scripts/montar_narrada.py   # final/audio-narrado.wav
ffmpeg -i cidades/floripa/final/video.mp4 -i cidades/floripa/final/audio-narrado.wav -map 0:v -map 1:a -c:v copy -c:a aac -shortest cidades/floripa/final/floripa-narrado.mp4
9

Render com luz real (opcional, GPU NVIDIA)

O mesmo vídeo com o relevo e os prédios iluminados por path tracing no Mitsuba 3: sombras entre os prédios, volume de maquete e prédios subindo no ano deles. O motor desenha por cima o HUD, os cartões e os efeitos, como sempre. Leva ~45 min para 95 s de vídeo (o render normal, ~5 min). Detalhes: docs/RENDER-LUZ-REAL.md.

Os dois formatos e o Blender (mesmo roteiro, mesmos cartões e efeitos; muda só o relevo e os prédios):

  • Padrão (deck.gl no Chrome): 0,07–0,11 s por quadro. Porto Alegre (95 s) em ~5 min, Florianópolis (6 min) em ~17 min. É o de todo vídeo e toda revisão.
  • Luz real (Mitsuba + motor por cima): ~0,75–0,95 s por quadro. Porto Alegre em ~45 min, Florianópolis em ~3 h (estimado). Versão de destaque, depois do padrão aprovado.
  • Blender (não usado): aqui roda só pelo processador, ~40 s por quadro. Porto Alegre levaria ~32 h e Florianópolis ~5 dias. Não há Blender oficial com GPU para Linux ARM64.
.venv/bin/pip install mitsuba==3.9.1 drjit==1.5.0 mapbox-earcut
bash motor/render_luz.sh cidades/floripa/cena cidades/floripa/final/floripa-mitsuba-video.mp4 64
Roadmap

Próximos passos

O que já existe e o que vem.

Feito
Porto Alegre e FlorianópolisMotor com globo, linha do tempo, cartões com fonte, cheias, cercos, fortalezas, pontes e aterros.
Novo
Luz real com Mitsuba (GPU)Sombras e luz de maquete por path tracing. Comparação medida: deck.gl × Blender × Mitsuba.
Próximo
Versão com imagens antigasGravuras e fotos históricas em domínio público entrando junto dos eventos.
Depois
Mais cidades e skillTransformar o fluxo numa skill para fazer uma cidade nova só com a pesquisa.