All Posts

Visualizing 86K GitHub Stars as Procedural Lobsters

I built a real-time 3D visualization that renders each of moltbot's 86,546 GitHub stars as a procedurally-generated lobster. Here's how I achieved 80K+ instances at 60 FPS in a single day.

Godot Optimization Procedural WebGL

I wanted to celebrate a project hitting 86,000 GitHub stars. A counter felt boring. A particle system felt generic. So I rendered each star as a procedurally-generated 3D lobster.

The whole thing took about six hours to build.

2,557 procedural lobsters arranged on a Fibonacci sphere

Why Lobsters?

The project I was visualizing—moltbot—has a molting/lobster theme baked into its identity. Instead of fighting that, I leaned into it. Each GitHub star becomes a lobster in an ever-growing swarm that users can orbit, zoom through, and scrub across time.

It’s memorable in a way that “86,546 stars” as text never could be.

The 80K Instance Problem

Rendering 86,000 individual scene nodes in Godot would destroy performance. Even at one node per lobster, you’d spend all your frame budget on scene tree overhead before drawing anything.

The solution is GPU instancing via MultiMesh. Instead of 86,000 nodes, you get one node with one draw call. The GPU handles the replication.

var multimesh = MultiMesh.new()
multimesh.mesh = lobster_mesh
multimesh.transform_format = MultiMesh.TRANSFORM_3D
multimesh.use_custom_data = true
multimesh.instance_count = 100000  # Pre-allocate once

I pre-allocate 100,000 instances at startup. Adding a new lobster just means updating its transform and making it visible—no memory allocation, no node creation, no performance spike.

Procedural Lobster Mesh

The lobster is entirely generated via code. No 3D modeling software involved. About 340 triangles per instance:

ComponentDetails
BodyElongated ellipsoid (8 segments × 5 rings)
Tail5 segmented plates + 3-plate fan
ClawsTwo articulated pincers (asymmetric sizing)
Legs6 pairs of jointed appendages
AntennaeTwo curving feelers

Procedural generation means I could iterate on the design in code without round-tripping through Blender. It also keeps the web export small—no external mesh files to load.

Avoiding the Clone Army

80,000 identical lobsters would look like a screensaver from 1998. The shader adds variation through custom data packed into each instance:

  • RGB channels: Color tinting (body hue, claw accent, belly lightness)
  • Alpha channel: Animation phase offset

Each lobster bobs and sways on a sine wave, but with a unique phase offset. The result is organic motion—no two lobsters move in sync.

float phase = INSTANCE_CUSTOM.a * TAU;
float bob = sin(TIME * 2.0 + phase) * 0.1;
VERTEX.y += bob;

Fibonacci Sphere Distribution

Where do you put 86,000 points on a sphere so they don’t look gridded or clumped?

The golden angle spiral. It’s the same pattern sunflower seeds use—mathematically proven to avoid visible patterns at any scale.

const GOLDEN_ANGLE = PI * (3.0 - sqrt(5.0))  # ≈ 2.39996

func get_position(index: int, total: int) -> Vector3:
    var y = 1.0 - (float(index) / (total - 1)) * 2.0
    var radius_at_y = sqrt(1.0 - y * y)
    var theta = GOLDEN_ANGLE * index
    return Vector3(
        cos(theta) * radius_at_y,
        y,
        sin(theta) * radius_at_y
    ) * sphere_radius

At 86K instances, you can zoom in anywhere and the distribution still looks natural.

Time Travel UI

The visualization isn’t static. A timeline lets you scrub through the project’s star history—from 13 stars on day one to 86,546 at present.

The data pipeline fetches star timestamps via GitHub’s GraphQL API (the REST API caps at 40K stars), aggregates them into daily counts, and exports as JSON. About 270 data points covering 62 days of growth.

Binary search finds the right count for any date: O(log n) for 270 points is effectively instant.

Web-First Decisions

The target was web browsers, which constrained several choices:

GL Compatibility renderer. Godot’s Forward+ looks better but requires WebGPU, which lacks browser support. GL Compatibility targets WebGL 2.0—works everywhere.

Unlit rendering. No shadows, no complex lighting. Consistent performance across devices and a cleaner visual style.

Touch controls. Pinch-to-zoom and drag-to-orbit work alongside mouse input.

Auto-zoom camera. As the swarm grows, the camera automatically pulls back to keep everything visible. Users can override manually.

What Took the Time

Six hours sounds fast. Here’s the rough breakdown:

PhaseTime
MultiMesh setup + camera controls1 hour
Procedural lobster mesh generation2 hours
Shader for instanced variation1 hour
Data pipeline + timeline UI1.5 hours
Polish (auto-zoom, colors, web export)0.5 hours

The procedural mesh was the most time-consuming part. Getting a lobster to look like a lobster through code—not Blender—required iteration. The rest was applying known techniques.

What I Learned

GPU instancing is the answer. Any time you need thousands of similar objects, reach for MultiMesh (or equivalent in other engines). The performance difference isn’t incremental—it’s categorical.

Procedural generation pays compound interest. Building the mesh in code felt slower initially, but it eliminated the art pipeline entirely. Changes were instant. The web export stayed small.

Fibonacci spirals are magic. I’d used golden angle distribution before, but seeing it hold up at 86K points was still satisfying. The math just works.

Web constraints clarify. Targeting the browser forced me to keep things simple: unlit shading, no post-processing, minimal file sizes. The result was a cleaner project.

Would I Do This Again?

Already thinking about it. The same technique could visualize any time-series data—commits, downloads, user signups—with thematic 3D objects instead of charts.

The lobsters made this project memorable. The instancing made it possible.

Source Code

The full source code is available on GitHub, including the Claude workflow I used to build it.