Thrine
Thrine (thrine.app) turns a 3D model into an embeddable, animated web component: load a `.glb`, pick a look, frame the shot, copy the snippet. The editor runs on three.js through @react-three/fiber, decoding Draco, Meshopt, and KTX2 payloads, and exposes environment lighting, tone mapping, shadows, bloom via an EffectComposer pass, and turntable, orbit, or scroll-driven motion as presets rather than raw renderer settings. Export writes a self-contained snippet—the whole configuration travels inside it and resolves the model from R2, so an embed keeps rendering even if the editor is offline. Models, presets, and the working session persist client-side; only the public model address is account-gated.
Build StackReact 19, TypeScript, Vite (Rolldown), three.js, @react-three/fiber, Zustand, Tailwind CSS, Cloudflare R2
Challenges
Getting first paint fast when the editor opens on a 1.4MB .glb behind ~440KB of JavaScript that has to download, parse, and mount before the model is even requested.
Exposing renderer controls—exposure, tone mapping, environment, bloom—as choices a non-3D user can make, without collapsing into a three.js settings panel.
Producing an embed that stays correct once it leaves the site, with no runtime dependency on the editor or its API.
Loading arbitrary user models whose Draco, Meshopt, or KTX2 payloads may be missing, partially referenced, or too heavy for a phone.
Solutions
Issued the boot model fetch from an inline script in the document head, so the transfer overlaps bundle parse instead of following it, with a plain fetch fallback if the URL drifts.
Reduced the renderer surface to named presets for lighting, material, and motion, keeping the raw values behind them.
Serialized the full configuration into the exported snippet and pointed it at a stable R2 address, so the embed carries its own state and re-export is the only way it changes.
Wired Draco, Meshopt, and KTX2 decoders into the GLTF loader, and reported dropped or unembedded references back to the user instead of failing silently.




