cascade

vencedor do Swift Student Challenge '26

Categories iPadOS
Stack SwiftUI, RealityKit, Accelerate, Swift Charts

overview

Todo ano a Apple sedia o Swift Student Challenge, uma competição que convida estudantes a construir apps criativos usando as APIs e tecnologias mais recentes da Apple. Quando as submissões para a edição de 2026 abriram, eu já sabia que queria fazer algo sobre espaço. Cresci lendo Júlio Verne, H.G. Wells e maravilhado por obras como 2001 do Kubrick, e até hoje passo uma noite ou outra perdido na Wikipedia lendo sobre novos planetas e missões. Em um desses mergulhos, por volta de 2024, descobri o conceito da Síndrome de Kessler, proposto por Donald Kessler e Burton G. Cour-Palais no final dos anos 1970: conforme lançamos mais objetos ao espaço, a densidade em certas regiões orbitais sobe a um ponto em que colisões se tornam exponencialmente mais prováveis. Cada colisão então gera estilhaços, que aumentam ainda mais a densidade, que aumentam ainda mais a taxa de colisões: uma reação em cadeia se desenrolando por anos e décadas. Sem controle, isso pode tornar faixas orbitais inteiras inutilizáveis, derrubando GPS, monitoramento meteorológico e internet via satélite junto. Visualizar isso me pareceu uma ideia que valia a pena construir, e, surpreendentemente, ainda não atacada por ninguém.

O Cascade é o resultado: um simulador educacional que combina controles em estilo arcade com simulação realista de gravidade. Construí o ambiente 3D em RealityKit e expus o máximo da simulação que consegui, incluindo o número de satélites e a variação das altitudes iniciais. O que mais amo em simuladores é poder mexer neles, e além de ser satisfatório de ajustar, essa abertura produz formas genuinamente impressionantes de assistir a uma colisão em cadeia se desenrolar.

Como o foco é educação, a interface precisava ser intuitiva. Optei por uma abordagem nativa usando um TabView para separar a simulação dos materiais de leitura. Adicionei uma aba 'Learn More' para a ciência e um gráfico em Swift Charts para mostrar o acúmulo de detritos desde os anos 1960. Para alcançar o máximo de pessoas, investi pesado em personalização e acessibilidade: rotacionar a câmera, dar zoom, ligar iluminação omni, recolorir satélites, detritos e fundo para melhorar o contraste, redimensionar ou esconder objetos e até esconder a própria Terra (o que, sinceramente, gera a visualização mais bonita). O VoiceOver também recebeu uma passagem dedicada, com rótulos descritivos espalhados pelo app.

O Cascade foi também um projeto de aprendizado para valer. Não sou físico, então me apoiei em ferramentas de IA para estudar e implementar a matemática da gravidade orbital, e fazer um iPad renderizar milhares de objetos colidindo de forma fluida exigiu uma quantidade significativa de otimização. Pelo aprendizado guiado, peguei o framework Accelerate para cálculos paralelos, construí um motor de física isolado em um actor para manter a interface responsiva e agrupei todos os detritos em uma única mesh dinâmica do RealityKit. Juntas, essas mudanças transformaram um rascunho em algo que roda confortavelmente no dispositivo sem o térmico tomar conta.

O Cascade foi selecionado como vencedor do SSC2026. Construir um app é difícil, ainda mais quando o tema exige pesquisa séria sobre otimização e arquitetura, então fiquei muito feliz com a vitória. Abaixo, coloquei um vídeo demo do app. O projeto também está disponível no GitHub.

0:00 / 0:00

tech stack

SwiftUI
interface e navegação
RealityKit
motor de renderização 3D
Accelerate
matemática paralela SIMD
Swift Charts
visualização de dados

code snippets

Use o framework Accelerate para cálculos vetorizados e manter o framerate alto. Com o layout Structure of Arrays (SoA), o acesso à memória é contíguo, tornando os cálculos gravitacionais muito mais eficientes.

func updatePhysics(dt: Float, earthMass: Float, killRadiusSq: Float, maxRadiusSq: Float) {
    guard activeCount > 0 else { return }
    let count = activeCount
    let n = vDSP_Length(count)

    withSixBuffers(&posX, &posY, &posZ, &velX, &velY, &velZ) { pX, pY, pZ, vX, vY, vZ in
        let xBase = pX.baseAddress!
        let yBase = pY.baseAddress!
        let zBase = pZ.baseAddress!
        let vxBase = vX.baseAddress!
        let vyBase = vY.baseAddress!
        let vzBase = vZ.baseAddress!

        // distSq = posX² + posY² + posZ²
        vDSP_vsq(xBase, 1, scratchA, 1, n)
        vDSP_vsq(yBase, 1, scratchC, 1, n)
        vDSP_vadd(scratchA, 1, scratchC, 1, scratchA, 1, n)
        vDSP_vsq(zBase, 1, scratchC, 1, n)
        vDSP_vadd(scratchA, 1, scratchC, 1, scratchA, 1, n)

        // invDist = rsqrt(distSq)
        var n32 = Int32(count)
        vvrsqrtf(scratchB, scratchA, &n32)

        // factor = -earthMass * dt * invDist³
        vDSP_vmul(scratchB, 1, scratchB, 1, scratchC, 1, n)
        vDSP_vmul(scratchC, 1, scratchB, 1, scratchC, 1, n)
        var coeff = -earthMass * dt
        vDSP_vsmul(scratchC, 1, &coeff, scratchC, 1, n)

        // Mask out-of-range particles
        for i in 0..<count {
            if scratchA[i] < killRadiusSq || scratchA[i] > maxRadiusSq {
                scratchC[i] = 0
            }
        }

        // vel += pos * factor
        vDSP_vma(xBase, 1, scratchC, 1, vxBase, 1, vxBase, 1, n)
        vDSP_vma(yBase, 1, scratchC, 1, vyBase, 1, vyBase, 1, n)
        vDSP_vma(zBase, 1, scratchC, 1, vzBase, 1, vzBase, 1, n)

        // pos += vel * dt
        var dt_val = dt
        vDSP_vsma(vxBase, 1, &dt_val, xBase, 1, xBase, 1, n)
        vDSP_vsma(vyBase, 1, &dt_val, yBase, 1, yBase, 1, n)
        vDSP_vsma(vzBase, 1, &dt_val, zBase, 1, zBase, 1, n)
    }
}

design choices

palette

space black #000000
starlight #FFFFFF
verde satélite #1BB83A
vermelho detrito #FF3B30

typography

SF Pro Display / títulos e texto primário
A rápida raposa marrom pula sobre o cão preguiçoso
SF Mono / dados e estatísticas monoespaçadas
A rápida raposa marrom pula sobre o cão preguiçoso

some fonts used in this project are proprietary and may not display correctly if they are not installed on your system.

rationale

O Universo é, em sua maior parte, completamente escuro. Isso significa que não apenas a cor predominante no Cascade é uma ausência de cor (até certo ponto), mas também que tive a oportunidade de explorar o uso significativo de cores para a interface e as mecânicas da simulação. Alinhado ao meu desejo de permitir personalização e ajuste fino da experiência, o Cascade permite ao usuário ajustar as cores dos satélites, detritos e do fundo, mas defini o verde para os satélites e o vermelho, que indica perigo, para os detritos.

Também decidi aproveitar a nova linguagem de design Liquid Glass. Ela permitiu que a interface ganhasse profundidade e dimensão permanecendo simples e eficaz. Em particular, é muito legal observar os detritos fluindo atrás dos botões e outros elementos da interface, sendo refratados pelo vidro. Uma das maiores vantagens do Liquid Glass, na minha opinião, é como ele permite que o app respire enquanto mantém o conteúdo em foco. Parece limpo, extremamente limpo, mas não estéril.

Decidi utilizar "assets" criados via código com as APIs nativas do RealityKit. Os únicos assets externos usados são mapas especulares e de textura para a Terra, sobrepostos em uma esfera do RealityKit. Por exemplo, os detritos são representados por tetraedros simples, que são as formas 3D mais simples que podem ser renderizadas. Esta foi uma escolha feita principalmente por razões de desempenho, mas acho que acabou dando ao app uma identidade visual única, já que passa uma vibe de radar. Os satélites sendo cubos verdes também ajudam com isso. As formas simples combinadas com as cores e a iluminação criam um visual muito distinto que ajuda a focar no evento da Síndrome de Kessler em si, em vez de desperdiçar processamento e renderização em gráficos excessivamente complicados.

credits

people

design · development
pedro wiezel

assets

textures
NASA/NOAA, Solar System Scope, Tom Patterson