What does building with AI look like when your application needs interactive planets, a working 3D interface, scientific content, and cinematic storytelling?
That’s what I’ve been exploring with Loupe, an interactive digital museum built with the help of Claude and Codex.
The application brings together exhibits about space, dinosaurs, human evolution, anatomy, jet engines, and Apollo 11’s lunar descent.
Each exhibit presents a different engineering challenge. A planetary explorer needs camera controls and coordinated viewing modes. A mission narrative needs scroll progression. An engine exhibit needs to make invisible processes understandable.
This post explains how those experiences work, how the application is structured, and where AI coding agents have been useful throughout development.
Loupe’s homepage connects visitors to exhibits with different subjects and interaction models.
1. The application idea: make curiosity interactive
The starting point was a question:
Could a website make exploring a scientific subject feel like using an instrument?
A visitor should be able to select an object, investigate a feature, change the view, and understand what changed.
In Loupe, that takes several forms:
| Exhibit | Visitor experience | Engineering challenge |
|---|---|---|
| Atlas of Worlds | Explore planets and compare their features | Textures, camera orientation, viewing modes, and state |
| The Engine Is a River | Investigate a turbofan and its flow stations | Model loading, particles, shaders, and linked explanations |
| Thirteen Minutes | Follow Apollo 11’s powered descent | Scroll progression, scene state, and narrative timing |
| Becoming Human | Explore human evolution through a cinematic journey | Content sequencing and visual continuity |
| Dinosaurs, Reconsidered | Inspect specimens and examine evidence | Asset preparation, annotations, and model switching |
This shaped an important product decision: the interaction should fit the subject.
The museum needs a coherent identity, but a planet and a lunar descent should not behave like the same page with different assets.
2. The stack behind Loupe
The core stack is familiar to many React developers:
- Next.js and TypeScript for routing, application structure, and typed contracts.
- React for the interface and component composition.
- Three.js and React Three Fiber for browser-based 3D.
- Drei for model loading, orbit controls, and scene utilities.
- GSAP for animation tooling.
- Zustand for shared exhibit state.
- Vitest and Playwright for automated verification.
- Vercel for hosting.
The interesting part is how these pieces divide responsibility.
The interface deals with choices: selecting a world, opening a panel, or changing a mode.
The renderer deals with continuous changes: camera movement, object rotation, and particle animation.
Keeping those responsibilities clear helps both performance and maintainability.
3. Structuring an exhibit as an application
An interactive exhibit quickly becomes more complicated than a component containing a model.
It has content, controls, visual modes, source information, loading behaviour, and state transitions.
For Atlas of Worlds, the repository separates those concerns into three broad layers:
content/space/
atlas.ts
atlas-assets.ts
atlas-asset-licenses.json
lib/space/
atlas-schema.ts
atlas-store.ts
atlas-scale.ts
world-focus.ts
components/space/
AtlasExperience.tsx
AtlasStage.tsx
AtlasCanvas.tsx
AtlasFallback.tsx
Content describes the worlds, assets, features, and explanations.
Logic handles calculations, state transitions, comparison policies, and focus behaviour.
Components turn those decisions into the visible experience.
From an application perspective, this means changing an explanation does not require editing camera code.
From a development perspective, it gives both humans and coding agents smaller, clearer problems to solve.
4. How Atlas renders an interactive planet
The globe, controls, feature selection, and field guide respond to the same exhibit state.
Atlas combines procedural geometry, surface textures, materials, and selected model assets.
For a roughly spherical world, a sphere provides the basic shape. Much of the visible detail comes from the texture and its treatment.
Additional modes introduce different layers or representations.
This makes the relationship between visual quality and asset complexity more interesting than “more polygons look better.”
A convincing result also depends on:
- Correct texture mapping.
- Appropriate lighting.
- Camera framing.
- Surface material settings.
- The scale of the object in the viewport.
The renderer caps its device pixel ratio, which limits rendering cost on high-density displays.
That choice matters because a canvas has to shade every rendered pixel. A visually small change in resolution can produce a substantial change in GPU workload.
The aim is to spend rendering effort where the visitor can perceive it.
5. One selection can change the whole interface
When someone selects a new planet, the application needs to update more than the globe.
The selection can affect:
- The available viewing modes.
- The active feature.
- The field guide.
- Lighting defaults.
- The comparison world.
- Camera orientation.
Atlas coordinates those changes through a Zustand store.
For example, changing worlds resets the viewing mode to the new world’s default, clears the selected hotspot, and issues a camera reset.
That prevents inconsistent states, such as displaying a feature from one planet while rendering another.
The camera commands also carry a sequence number. That allows repeated actions—such as pressing Reset twice—to remain distinct commands.
These details are easy to overlook in an initial prototype. They become important once people start exploring freely.
6. Keeping animation out of React’s update cycle
Continuous animation should not require a React state update for every frame.
The project uses references and React Three Fiber’s useFrame to update scene objects directly.
A simplified example:
const mesh = useRef<THREE.Mesh>(null);
useFrame((_, delta) => {
if (!motionEnabled || !mesh.current) return;
mesh.current.rotation.y += delta * rotationSpeed;
});
Here, React controls whether motion is enabled. The render loop updates the rotation.
Using delta makes movement depend on elapsed time, which helps keep the intended speed consistent across different frame rates.
Camera transitions follow a similar pattern. The interface chooses the destination, and the renderer interpolates toward it.
The engine exhibit also stops automated camera movement when the visitor begins using the orbit controls.
That is a small implementation detail with a direct product consequence: the camera respects the visitor’s input.
7. Explaining an engine with shaders
Selecting a flow station connects a physical location on the engine to its explanation and operating values.
The engine exhibit combines a loaded GLB model with procedural visualisations.
Its airflow particles have attributes such as phase, angle, radius, and speed. A shader uses those attributes, time, and engine parameters to calculate their positions and colours.
This avoids sending thousands of individual particle-position updates through React.
The implementation also adjusts particle counts based on the canvas width. The bypass stream, for example, uses more particles in the wider layout than in the narrower one.
The visitor still sees the flow relationship, while the renderer performs less work on a smaller display.
There is also a content responsibility here: the visualisation is an explanatory model. Its appearance should not imply that it is live telemetry or a computational fluid dynamics simulation.
Engineering the visual and explaining its limits are both part of building the feature.
8. Turning scrolling into a narrative control
Thirteen Minutes introduces the mission before the visitor enters the descent narrative.
For Apollo 11, the central interaction is progression through a sequence of events.
The architecture separates:
- Interpreting scroll position.
- Identifying the active narrative section.
- Resolving scene state.
- Moving the camera.
- Updating the mission interface.
One small utility determines which section contains the viewport’s centre:
export function centeredBeatIndex(
bounds: ReadonlyArray<{ top: number; bottom: number }>,
viewportHeight: number,
) {
const center = viewportHeight / 2;
const index = bounds.findIndex(
({ top, bottom }) => top <= center && bottom > center,
);
return index >= 0 ? index : null;
}
Because it receives measurements and returns an index, this function can be tested without mounting a 3D scene.
That separation also helps debugging. An incorrect camera view may originate in scroll interpretation, scene progression, or camera interpolation. Those are different problems, even when the visitor experiences them as “the animation feels wrong.”
9. The difficult part: everything around the first render
The first working model is a milestone. Making it work well inside the application takes more iteration.
During development, the dinosaur exhibit exposed issues including missing skeletons, incorrect model presentations, and delays when switching specimens.
One particularly noticeable behaviour was a still image appearing before the interactive model.
Displaying something quickly can be useful, but the transition can also make the experience feel as though it loads twice.
That taught me to evaluate performance through the complete interaction:
What happens between clicking a specimen and being able to inspect it?
Download time matters. So do decoding, GPU preparation, camera framing, and the visual handoff.
Preloading can help, but loading everything upfront introduces bandwidth and memory costs. It needs to be applied selectively.
I also would not claim a Lighthouse score or universal frame rate without measurements across representative devices. The application’s behaviour is the evidence that matters.
10. Where Claude and Codex help
The strongest benefit of Claude and Codex has been their ability to help work through connected problems across the repository.
A rendering bug may involve several files: the loader, scene component, state store, and fallback boundary.
Agents can help inspect that path, propose an implementation, make coordinated changes, and support verification.
The prompts become more useful when they describe observable behaviour and constraints.
For example:
Selecting a feature should bring it into view while preserving manual orbit controls. Inspect the coordinate conversion and focus behaviour, explain the cause of the current failure, and implement a focused fix.
That gives the agent a specific outcome to pursue.
The development loop becomes:
- Define what the visitor should experience.
- Give the agent the relevant context.
- Implement a bounded change.
- Inspect the application.
- Report specific failures.
- Refine and verify.
The human contribution remains substantial: choosing the experience, judging the result, checking the scientific representation, and deciding what deserves further work.
11. Making quality requirements explicit
Loupe keeps exhibit standards in the repository.
They ask questions such as:
- What should the visitor understand?
- Does each control have meaningful behaviour?
- Are sources and reconstruction limits visible?
- What happens on mobile?
- How does the experience behave when an asset fails?
This makes expectations available across development sessions.
The project also includes tests for state, calculations, content, and browser interactions. But automated checks are only one part of verification.
A test can confirm that a focus calculation returns the expected orientation. Visual inspection still needs to confirm that the selected feature is understandable on screen.
The same applies to accessibility and fallbacks. Atlas checks WebGL availability and includes an error boundary. The Apollo experience considers reduced motion, reduced data, and viewport proximity.
These behaviours help the application remain useful outside the ideal desktop scenario.
12. What this project showed me about AI-assisted development
Building Loupe has made me more willing to attempt projects that cross several technical disciplines.
Claude and Codex help with the implementation capacity needed to explore those ideas: components, state, graphics, asset integration, debugging, and tests.
The application improves through the combination of that capacity and deliberate product decisions.
What should be interactive? What needs explanation? Where does motion help? Which details justify their performance cost?
Those decisions shape the result as much as the code.
If you explore it, I’d especially appreciate feedback on model loading, camera controls, and how the exhibits behave on your device.
What application have AI coding tools made you willing to build?
Suggested DEV tags: ai, webdev, react, showdev
Publishing note: upload the four screenshots to DEV and replace their local paths with the uploaded image URLs.




Top comments (0)