惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
Martin Fowler
Martin Fowler
博客园_首页
量子位
T
Tailwind CSS Blog
博客园 - Franky
G
Google Developers Blog
D
DataBreaches.Net
Vercel News
Vercel News
B
Blog
Recent Announcements
Recent Announcements
S
SegmentFault 最新的问题
M
MIT News - Artificial intelligence
爱范儿
爱范儿
博客园 - 【当耐特】
The Cloudflare Blog
H
Help Net Security
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed
C
Check Point Blog
有赞技术团队
有赞技术团队
Microsoft Security Blog
Microsoft Security Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
I Built a 3D Solar System in 300 Lines of React (No Game ...
Devanshu Bis · 2026-05-19 · via DEV Community

Pull up a browser. Drag your mouse. Watch eight planets orbit the Sun, axes tilted, Saturn's rings catching the light.

That's not a game engine. That's not Unity. That's 300 lines of React.

If your mental model of "3D programming" is "scary C++ matrices and a 600-page OpenGL textbook," you're a decade out of date. WebGL has shipped in every browser since 2014. Three.js wraps the boring math. React Three Fiber lets you write the scene as components, the same way you write HTML. The whole pipeline is <mesh>, <sphereGeometry>, <meshStandardMaterial> — three tags, you've made a planet.

Today I'll show you the whole thing.

The core insight: a scene is a tree

Every 3D scene — every Pixar movie, every video game, every product configurator — is the same shape: a tree of objects, where each node has a position, a rotation, a scale, and zero or more children. That's it. The Sun is the root. Earth is a child positioned 9 units to the right. The Moon is a child of Earth, positioned 1 unit further right. Rotate Earth and the Moon comes along for the ride, because it's a child.

In Three.js you build this tree imperatively:

const sun = new THREE.Mesh(geom, mat);
const earth = new THREE.Mesh(geom2, mat2);
earth.position.x = 9;
sun.add(earth);
scene.add(sun);

Enter fullscreen mode Exit fullscreen mode

In React Three Fiber, the tree IS your component tree:

<mesh>           {/* sun */}
  <sphereGeometry />
  <meshBasicMaterial color="yellow" />
  <mesh position={[9, 0, 0]}>    {/* earth, child of sun */}
    <sphereGeometry args={[0.5]} />
    <meshStandardMaterial color="blue" />
  </mesh>
</mesh>

Enter fullscreen mode Exit fullscreen mode

That's the whole conceptual leap. Once you see "the React tree is the Three.js scene graph," the rest is naming things.

The trick that makes orbits cheap

Naïve orbit code looks like this:

useFrame((_, dt) => {
  angle += speed * dt;
  earth.position.x = Math.cos(angle) * 9;
  earth.position.z = Math.sin(angle) * 9;
});

Enter fullscreen mode Exit fullscreen mode

That works, but you're doing two trig calls per planet per frame in JavaScript. 60 fps × 8 planets = 960 sin/cos per second in slow JS.

There's a better way. Put the planet inside a pivot group at the origin. Place the planet at (distance, 0, 0). Rotate the group, not the planet.

<group ref={orbitRef}>                    {/* this group spins → orbit */}
  <mesh position={[distance, 0, 0]}>      {/* planet stays put in local space */}
    <sphereGeometry args={[radius]} />
    <meshStandardMaterial color={color} />
  </mesh>
</group>

useFrame((_, dt) => {
  orbitRef.current.rotation.y += speed * dt;   // ONE addition
});

Enter fullscreen mode Exit fullscreen mode

Now you're doing one addition per planet per frame in JavaScript and zero trig. Three.js's internal matrix update handles the rotation in compiled C++ inside the GPU pipeline. The math still happens — it just happens in the right place.

Same trick for axial rotation: a child group inside the planet rotates on its own Y axis. Tilt the wrapper group on the X axis and Uranus is suddenly tipped 98° like real Uranus. The whole solar system is six nested groups doing addition.

Lighting: three lines, instantly 3D

If you skip lighting, every planet looks flat — like a coloured paper disc. Add one <pointLight> at the Sun's position and use meshStandardMaterial for the planets:

<pointLight position={[0, 0, 0]} intensity={2.5} distance={120} />

<mesh>
  <sphereGeometry args={[0.5]} />
  <meshStandardMaterial color="blue" roughness={0.7} />
</mesh>

Enter fullscreen mode Exit fullscreen mode

meshStandardMaterial is physically-based — it reads the light, bounces it off the surface based on roughness and metalness, and shades the half facing the light bright while the half facing away goes dark. Three lines. Instant 3D.

Pro tip: don't use meshStandardMaterial for the Sun itself. The Sun emits light, it doesn't receive it. Use meshBasicMaterial, which ignores all lights and shows the colour you set, flat. Otherwise you'll have a yellow sphere with a dark side, which looks wrong.

OrbitControls: 80% of the polish for free

Drei (the R3F helper library) ships an <OrbitControls /> component. Drop it in your <Canvas> and you get:

  • Drag to rotate the camera around the scene
  • Scroll to zoom
  • Pinch on mobile
  • Two-finger rotate
<OrbitControls enablePan={false} minDistance={6} maxDistance={80} />

Enter fullscreen mode Exit fullscreen mode

Three lines, all of "drag to look around" is done. This is the kind of thing that takes a junior developer two weeks in raw WebGL and 30 seconds in R3F. Use the helpers.

HTML overlays beat in-canvas UI

The temptation when you're new to 3D is to put every UI element inside the 3D scene — billboards, sprites, text geometry. Don't. Mount your <Canvas> full-bleed and stack regular HTML on top with position: absolute.

<div className="shell">
  <header className="hero">...</header>
  <Canvas>...</Canvas>
  <aside className="info-panel">...</aside>
</div>

Enter fullscreen mode Exit fullscreen mode

The info panel that slides in when you click a planet is just a styled <aside>. The speed slider is <input type="range">. Your CSS skills transfer 1:1. The 3D part stays focused on 3D.

What I learned actually building this

Real takeaways from an afternoon of this:

1. Three.js is huge but the surface you need is small. The full Three.js bundle is ~600KB. You will use maybe 12 of its 400+ classes. Scene, Mesh, SphereGeometry, MeshStandardMaterial, PointLight, PerspectiveCamera, OrbitControls. That's most of it.

2. Real scale is the enemy. The Sun is 109× the radius of Earth. Neptune orbits ~30× further than Earth. If you use real ratios, the Sun fills the screen and Neptune is a single pixel. Cheat the visuals. Show real numbers in the info panel.

3. useFrame runs 60Hz, so don't allocate. Every frame, that callback fires. If you new Vector3() inside it, you're creating garbage 60 times per second. Either mutate refs you already have, or hoist allocations outside.

4. delta is your friend. R3F's useFrame((_, delta) => ...) gives you seconds since last frame. Multiply your speed by delta and your animation runs the same on a 60Hz laptop and a 144Hz gaming monitor. Without delta, your planets fly off the screen on a high-refresh display.

5. dpr={[1, 2]} is the mobile performance switch. Devices with retina displays would normally render at 3× resolution and tank the FPS. Capping at 2× looks identical to the eye and triples your frame rate on phones.

Why this matters

3D in the browser used to be a specialty — game studios, big agencies, NASA visualizations. It's not specialty anymore. Product configurators, real estate walkthroughs, data visualizations, NFT galleries, classroom physics demos — every web product is starting to have a 3D moment.

R3F is the lever that makes 3D approachable for people who already write React. You don't have to learn imperative scene-graph plumbing. You already know how trees of components work — you're doing 3D, you just have a different leaf type.

So go play. Open the live demo, click each planet, scroll out and look at the layout from the side. Then clone the repo and change a number. Make the Sun blue. Add a moon to Earth — wrap an Earth-sized sphere in an outer group, position the moon (1.2, 0, 0), and watch it follow. That's the entire mental model. You'll be making your own scenes within an hour.

Try it / fork it

🌐 Live: https://threejs-from-zero.vercel.app
🐙 Code: https://github.com/dev48v/threejs-from-zero

This is Day 36 of TechFromZero — a 50-day series where I build one tech from scratch every day with step-by-step commits you can read like a textbook. Yesterday was a voice AI tutor (Web Speech → Gemini → TTS). Tomorrow we're building a multi-agent AI orchestration that has agents argue with each other.

🌐 See all days: https://dev48v.infy.uk/techfromzero.php

Talk to you tomorrow.