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.

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.
Feitos com este motor, no canal Aventuras INEMA. Cada cidade tem versão sem narração e narrada.
6 min: sambaquis, Dias Velho, fortalezas, açorianos, a invasão espanhola de 1777, as pontes e os aterros. Narrada: aqui. Abrir no YouTube
7 min: tupinambás, França Antártica, a Corte em 1808, Império e República. Narrada: aqui. Abrir no YouTube
6 min: da aldeia jesuíta à fazenda de Santa Cruz e ao porto. Narrada: aqui. Abrir no YouTube
5 min, com a câmera nas ruas e nos prédios históricos. Narrada: aqui. Abrir no YouTube
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
1min35, narrado (o piloto): fundação, cercos farroupilhas, a cheia de 1941, o Muro da Mauá e a cheia de 2024. Abrir no YouTube
Dados reais em vez de imagens inventadas.
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.
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.
Globo com zoom até a cidade, linha do tempo, frotas navegando, cercos, fortalezas, pontes desenhadas, aterros crescendo em cor e cheias subindo.
Tudo roda na sua máquina: deck.gl no Chrome com a GPU, cerca de 0,1 s por quadro em 1080p.




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.
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
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 NVIDIAO 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)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).
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 ..
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
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
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 } ] }
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
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
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/
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
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
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):
.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
O que já existe e o que vem.