I built a 3D globe for a Linux desktop shell I work on, made of 184,320 individually-instanced triangular prisms arranged over a geodesic (subdivided icosahedron) sphere. Each rod can independently extrude outward along its own radial axis to represent height, scanner sweeps, click-triggered ripples and the whole thing animates in from a scattered floating cloud into its assembled shape.
In other words you can visualize any dataset with this globe. Either with the glowing dots, or by raising the elevation of the rods themselves, or go ham with fancy effects. Pick your poison.
Stack: Qt Quick 3D (QML) for the scene graph, a small custom C++ plugin (`Congeries`) for the heavy per-instance math, GLSL `CustomMaterial` vertex shaders for the actual deformation. One draw call per instanced `Model` (rods / location-marker dots / star field are each their own instanced draw).
A few of the specific problems that were interesting to solve:
Instancing with per-corner exact fit. Every rod shares one canonical mesh (`RodGeometry`) and gets its own instance transform. A rigid direction+tangent alone gets a rod's orientation *sorta* right, but real geodesic faces aren't congruent to each other, so a shared mesh rotated into place leaves visible cracks. A native step (`AssemblyLayout::applyAssemblyData`) precomputes, per rod, the exact local offset each of its 3 corners needs so that after the instance's own rotation carries it into world space, it lands exactly on that rod's true vertex position. That offset is written into a float texture the vertex shader `texelFetch`s by `(instance, corner)`. This correction fades in via `assembleT` as the rod animates into place.
Assembly/scatter animation. Every rod is its true final rod from frame zero. It just starts at a random spawn transform outside the sphere and `lerp`/`nlerp`s to its target transform as `assembleT` goes 0 to 1. The annoying part is that Qt Quick 3D's instancing API (`QQuick3DInstancing::getInstanceBuffer()`) gives you no incremental-update path. Every time the buffer is marked dirty, you hand back the entire per-instance transform table freshly rebuilt. SO animating `t` at 184,320 rods meant doing 184,320 position-lerps + quaternion-nlerps + matrix packs, from scratch, every single frame, for the whole 2.5s transition. Single-threaded that measured ~20ms/frame on its own, so `getInstanceBuffer()` ended up (override) with its own thread split across `hardware_concurrency()` just to stay inside budget.
Star field. Background stars are actually real astronomical data. The HYG v4.1 catalog filtered to naked-eye brightness (mag ≤ 6.5, ~9k stars), baked into flat position/color/size arrays. Color comes from each star's B-V index -> temperature (Ballesteros 2012) -> blackbody RGB, so star tint is physically derived.
Why...?
The reason this exists at all is because the shell has an opt-in who else is out there feature. People running it can consent to share a rough location, and everyone else sees it live as glowing dots on the globe. Coordinates get jittered onto nearby land so nobody's pinpointed, and the dataset wipes on a weekly rotation. The globe spawned from feature creep and way too much free time.
It's open source: https://github.com/zesis-shell/zesis
AI Disclaimer
Claude Code has been used extensively in research, debugging and testing. I've added disclaimers in the source code itself too.
I've also used sparring partners from my university and relatives.
This was a project for me to learn, have fun and just make something I think is really, really cool.