O que é isso?
O conjunto de Mandelbrot é o exemplo mais famoso de fractal: um objeto com detalhe
infinito. Cada ponto c do plano complexo entra ou não no conjunto conforme a
iteração z → z² + c (começando com z = 0) fica limitada ou escapa para o
infinito. As cores mostram quantas iterações cada pixel levou para escapar.
A graça é dar zoom: não importa o quanto você aproxime, sempre há estrutura nova. Este app foi feito para aguentar esse “sempre” — o zoom não para em 100.000×, vai até onde sua paciência aguentar (o limite configurado é 10^1200).
Como funciona
A GPU faz o trabalho pesado (WebGL)
Cada pixel é independente dos demais: é o cenário perfeito para a GPU. A conta
z → z² + c roda num fragment shader WebGL2, milhões de pixels em paralelo.
Para não travar o navegador em cenas pesadas, as iterações rodam em blocos: o
estado de cada pixel (z, contagem, status) fica guardado em texturas float
que fazem ping-pong entre passadas, e a imagem vai se completando
progressivamente na tela.
O problema: float32 acaba rápido
A GPU só calcula nativamente com float32, que tem ~7 dígitos de precisão. Por
volta de zoom 10⁵, pixels vizinhos passam a ter o mesmo valor de c e a imagem
vira blocos. Precisão dupla no shader não existe em WebGL, e emular precisão
arbitrária por pixel na GPU seria lento demais.
A saída: teoria de perturbação
O truque (K. I. Martin, 2013) é separar o problema em duas partes:
- Uma órbita de referência
Z₀, Z₁, Z₂, …é calculada com precisão arbitrária na CPU — aqui, aritmética de ponto fixo comBigInt, num web worker para não travar a página. São os únicos números “caros” de todo o processo. - Cada pixel só acompanha a diferença
δ = z − Zpara a referência. A álgebra fecha bonito:δ′ = 2·Z·δ + δ² + δc. Essa recorrência só usa números pequenos e roda inteira em float na GPU.
Ou seja: a precisão arbitrária entra uma vez por imagem (na referência), e a GPU segue trabalhando com números baratos. É o mesmo esquema dos renderizadores sérios de deep zoom, como Kalles Fraktaler e fraktaler-3.
Quando a órbita do pixel se aproxima demais de zero (perto de um minibrot) ou sobrevive além do fim da referência, o pixel sofre rebase (técnica do usuário Zhuoran, 2021): o δ vira o valor completo e volta a acompanhar a referência do início. Isso elimina os “glitches” clássicos da perturbação sem precisar de múltiplas referências.
E quando nem o expoente do float32 aguenta?
Em zoom além de ~10²⁷, o δ de um pixel é menor que o menor float32 positivo (~10⁻³⁸) — o número vira zero antes de perder precisão. A solução é o formato floatexp: mantissa float32 + expoente inteiro separado, com soma e multiplicação implementadas à mão no shader. É mais lento, então o app escolhe o modo conforme a profundidade:
| zoom | modo |
|---|---|
| até ~10³ | float32 direto |
| até ~10²⁶ | perturbação com δ em float32 |
| além | perturbação com δ em floatexp |
Detalhes de engenharia
- O centro da imagem vive em
BigIntde ponto fixo (a precisão cresce com o zoom), então arrastar a 10^500 continua exato. - Enquanto você arrasta ou dá zoom, a última imagem pronta é reprojetada (transformada afim) como preview; o render de verdade recomeça quando o gesto assenta.
- A referência é reaproveitada entre movimentos próximos: dar zoom perto do mesmo ponto não recomputa a órbita.
- A órbita de referência também é guardada em floatexp na textura — perto de minibrots ela passa tão perto de zero que float32 puro descartaria exatamente a informação de que o rebase precisa.
- O link do botão “compartilhar” carrega as coordenadas completas em hexadecimal, com quantos dígitos forem necessários.
Limites honestos
Perto da fronteira do conjunto a dinâmica é caótica: com milhares de iterações, a contagem exata de escape de um pixel depende da precisão usada — em qualquer renderizador em ponto flutuante. A estrutura da imagem é a mesma; faixas de cor individuais podem diferir de renders feitos com outra aritmética. Em profundidade extrema com muitas iterações, o render leva alguns segundos (a imagem vai aparecendo aos poucos). Aproximação por séries (BLA) e antialiasing ficam para uma próxima versão.
Referências
- K. I. Martin, SuperFractalThing (2013) — a formulação original da perturbação.
- Zhuoran, Another solution to perturbation glitches (fractalforums, 2021) — o rebasing.
- Claude Heiland-Allen, fraktaler-3 e o blog mathr.co.uk — floatexp e muito do estado da arte.
- Inigo Quilez — as paletas por cosseno.