Dev (Back & Front)ARTIGO

Kutta no navegador: mesmo código, outro orçamento de frame

Kutta no navegador: mesmo código, outro orçamento de frame
Imagem: Cesar Gimenes

Kutta agora roda dentro de uma aba do navegador. Mesmo código do desktop, GOOS=js GOARCH=wasm, nenhum fork, nenhum arquivo duplicado com uma versão “web” do solver.

Kutta soltando vórtices a 20 graus de ângulo de ataque

O que não é de graça é a velocidade. Um passo do solver custa 6,1 ms no wasm contra 2,3 ms nativo, medido no grid de 360×200 que o app usa, num Mac M4, com o mesmo solver compilado para os dois alvos na mesma máquina. Fator 2,6.

Esse número decide o ritmo da simulação. O ambiente do navegador decide o resto.

Em 60 TPS, cada tick tem 16,7 ms

O build nativo avança três passos do solver por tick. São 6,9 ms, e ainda sobra tempo para a fumaça, o campo de cor e os painéis.

Três passos no wasm dariam 18 ms. Estoura o orçamento do tick antes de desenhar qualquer coisa, e o navegador começa a engasgar. Então o build web anda um passo por tick.

O escoamento evolui mais devagar no relógio de parede. A física por passo é idêntica: o mesmo D2Q9, a mesma colisão BGK, os mesmos bounce-backs. Só o tempo simulado por segundo de tela é menor. Suave e lento lê melhor que rápido e picotado, e para uma ferramenta qualitativa isso não custa nada de verdade.

O default mora em dois arquivos que o build tag separa, substeps_js.go e substeps_other.go:

go
//go:build js
var substeps = 1
go
//go:build !js
var substeps = 3

É var, não const, de propósito. A flag -substeps sobrescreve os dois sem recompilar. Isso veio de um PR do johncohn para dar folga em hardware devagar, um Raspberry Pi rodando o Kutta em quiosque, e acabou servindo igualzinho para o navegador. Uma flag que troca velocidade de evolução do escoamento por folga de CPU serve nos dois lados.

Uma aba não escreve em caminho nenhum

O Kutta desktop usa diálogos para abrir e salvar cenas .afoil, importa SVG pelo menu e tem menu nativo. Na aba, abrir ou importar vira drag-and-drop; salvar vira download; o menu nativo sai.

A separação é por constante, não por if de runtime:

go
//go:build js
const onWeb = true

Constante para o compilador jogar fora o galho que não se aplica, em vez de carregar código de filesystem que nunca vai rodar. Três lugares no app perguntam por onWeb e o resto do código não sabe que existe navegador.

“Salvar” no navegador virou entregar bytes para o navegador baixar: monta um Uint8Array, embrulha num BlobURL.createObjectURL, cria um <a download>, clica nele por código e remove. A implementação inteira cabe em webfile_js.go.

E aí tem a cicatriz. No Kutta, revogar a object URL logo depois do clique cancelava o download que ainda não tinha começado a ler. O arquivo simplesmente não aparecia, sem erro, sem aviso. A saída foi deixar a URL envelhecer num setTimeout de 60 segundos e morrer sozinha.

go
js.Global().Call("setTimeout", release, 60000)

Sessenta segundos é chute com folga. Não achei número melhor e não vou fingir que achei.

O tamanho

kutta.wasm tem 17,5 MB. Servido com gzip cai para 4,25 MB.

É muito. É o runtime do GoGo23 conteúdosEntendendo o Green Tea GC do Go 1.26Dev (Back & Front) · mai 2026Função recursiva em Go para acessar valores em mapas aninhadosDev (Back & Front) · set 2025Publicando projeto desenvolvido em Golang em um server grátisDev (Back & Front) · mar 2025Ver tudo em Dev (Back & Front) inteiro mais o Ebitengine dentro de um download que o visitante faz antes de ver o primeiro pixel. -ldflags="-s -w" tira os símbolos e não muda o jogo. Quem quer wasm pequeno não escreve em Go, escreve em C ou Rust e aceita o resto do custo.

Eu aceito os 4 MB. O que eu ganho do outro lado é a mesma base de código gerando builds para Windows, macOS, LinuxLinux34 conteúdosKali Linux em um Servidor VPS: como, quando e por que usar?DevSecOps · dez 2024Construindo um Windows Service ou Linux Daemon com Worker Service & .NET Core – Parte 2Dev (Back & Front) · jul 2020Criando uma WebApi utilizando .NET, Linux e VSCodeDev (Back & Front) · ago 2019Ver tudo em DevSecOps e navegador, com um solver escrito uma vez só.

O que isso muda na distribuição

A página do produto passou a rodar o simulador dentro dela. Sem download, sem instalador, sem “confie em mim e execute este .exe”: crgimenes.itch.io/kutta carrega o wasm e o túnel de vento aparece ali.

Os executáveis continuam lá, um arquivo por plataforma, sem instalador. Pague quanto quiser, incluindo zero.

O fonte segue MIT em github.com/crgimenes/kutta, e o build web é uma linha:

sh
GOOS=js GOARCH=wasm go build -trimpath -ldflags="-s -w" -o web/kutta.wasm .

Trabalha com tecnologia desde a década de 90. Já atuou na área de educação e participou de projetos de mobilidade de grande volume para laboratórios farmacêuticos. Criou games tanto para PC, como para iOS. Hoje está direcionando seus esforços em plataformas de Sistemas Embarcados, IoT, microservices e cloud computing. É um entusiasta de tecnologias como Golang e Docker.

Ver perfil