Best for
Developers building third-person 3D exploration experiences, character demos, or interactive 3D product showcases in the browser
Stack
A ready-made Third-Person Starter UI you can fork, run, and customize with the prompt pack below.
What's actually inside
The honest engineer's breakdown — what the Third-Person Startertemplate does, how it's wired, and where it's opinionated.
The Third-Person Starter is the most architecturally ambitious template in this game category. R3FCanvas is a @react-three/fiber Canvas component configured with shadows enabled and a camera rig that hands off to ThirdPersonCamera — an orbit control constrained to follow the PlayerCharacter mesh position from behind and above. The camera doesn't drift freely like in a typical drei OrbitControls setup; it's locked to the player's bounding sphere offset, which is what makes the third-person feel work in a game context.
MovementController reads WASD keyboard state via a ref-based input tracker (not useState — using state would cause render thrashing every keyframe) and updates the PlayerCharacter position each tick via useFrame from @react-three/fiber. GroundPlane is a large flat mesh with receiveShadow so the player casts a ground shadow, and SceneEnvironment uses a drei Environment preset (e.g. 'sunset', 'forest') to inject HDRI lighting without managing light objects manually. AnimationStateMachine tracks 'idle' | 'walking' | 'running' states based on input velocity magnitude — a clean pattern to extend when you load a real GLTF character with animation clips.
Honest caveats to know before forking: The PlayerCharacter is a placeholder box/capsule mesh, not a real humanoid model — you'll need to load your own GLTF via the first prompt below. ThirdPersonCamera has no collision avoidance, so it will clip through walls if you add scene geometry close to the player. And critically: V0's esm.sh preview sandbox reliably fails to load Three.js and react-three-fiber for 3D templates — the correct workflow is fork → deploy to Vercel immediately → test only on the live URL. Do not spend time debugging in V0 preview for this template.
Key UI components
R3FCanvas@react-three/fiber Canvas with renderer, shadow maps enabled, and camera configured for third-person perspective
ThirdPersonCameraOrbit-style camera offset behind and above PlayerCharacter, constrained to follow the player target position
PlayerCharacter3D box/capsule mesh placeholder with animation state; swap for a GLTF model using the first prompt
MovementControllerRef-based WASD keyboard state tracker driving PlayerCharacter position via useFrame each tick
GroundPlaneLarge flat mesh with receiveShadow providing the world floor for the character to walk on
SceneEnvironmentdrei Environment preset providing HDRI lighting and skybox without manual Three.js light setup
AnimationStateMachineTracks 'idle' | 'walking' | 'running' states based on MovementController velocity for animation clip switching
HUDOverlay2D React div overlay rendered outside R3FCanvas for health bars, crosshairs, or debug info
Libraries it leans on
@react-three/fiberReact renderer for Three.js — provides R3FCanvas, useFrame, useThree for the scene lifecycle
@react-three/dreiHelpers: OrbitControls for ThirdPersonCamera, Environment for HDRI lighting, useGLTF for character models, useAnimations for clips
Three.jsCore 3D engine — mesh geometry, materials, shadow maps, Raycaster for camera collision avoidance
Fork it and get it running
This is an Advanced template. Fork and deploy immediately — V0 preview doesn't run Three.js reliably. Your real testing environment is the Vercel URL, not the V0 editor.
Fork the template on v0.dev
Open https://v0.dev/chat/community/FU9hG5OnPu0 and click the 'Fork' button in the top-right area of the community page. This copies the full template — R3FCanvas, ThirdPersonCamera, MovementController, and all supporting components — into your V0 project. Forking consumes no credits.
Tip: Even if the Preview tab shows a blank screen or a loading spinner, the fork worked correctly — 3D templates rarely render in V0's sandbox.
You should see: The V0 editor opens with the Third-Person Starter project loaded; code is visible in the Code tab.
Publish to Vercel immediately — don't test in Preview
Click the Share button at the top-right of the V0 editor, then the Publish tab. Click 'Publish to Production'. Wait 30-60 seconds. This is the most important step for 3D templates: V0's esm.sh preview sandbox cannot reliably load @react-three/fiber and Three.js. The live Vercel URL is the only correct way to test this template.
Tip: If the build fails with 'Module not found: @react-three/fiber', copy the fix_prompt from the gotchas section and paste it into V0 chat before publishing again.
You should see: A live .vercel.app URL is available; the 3D scene loads with a character and ground plane visible.
Test WASD movement and camera on the live URL
Open the Vercel URL in Chrome or Firefox on a desktop. Use WASD to move the PlayerCharacter and watch the ThirdPersonCamera orbit and follow. Verify the GroundPlane shadow appears under the character and the SceneEnvironment lighting changes the scene atmosphere. These are the two signals that the renderer, shadows, and HDRI are all working correctly.
Tip: Safari may show reduced performance for Three.js scenes on some hardware — Chrome is the most reliable browser for testing.
You should see: Player moves in all four directions, camera follows smoothly, shadow appears on ground, skybox is visible.
Connect GitHub to pull 3D code locally
In the V0 editor, open the Git panel and connect your GitHub account. Link the project to a new repository. V0 creates a branch named v0/main-{hash} for each edit session. Pull this branch locally to work with your own GLTF character models: place .glb files in /public/models/ and use the prompt below to load them. Local development lets you run npm run dev and test the 3D scene with hot reload in a real Node.js environment.
Tip: Run npm run build locally before pushing — 3D templates can have dependency issues that only surface at build time.
You should see: The repository is on GitHub and you can pull it locally; npm run dev starts the Next.js dev server with the 3D scene.
Add a custom GLTF character model
Place your character .glb file at /public/models/character.glb. Use the first prompt from the prompt pack to swap the placeholder box mesh for the real model. After making the change in V0 chat, publish again (Share → Publish → Publish to Production) and verify on the Vercel URL. For physics, install @react-three/rapier and use the fifth prompt to wrap the scene in a Physics provider — but test this entirely on the deployed URL.
Tip: Free GLTF character models are available at Mixamo (mixamo.com) — download with T-pose + animations included.
You should see: Your GLTF character model appears at the player position and moves with WASD input.
The prompt pack
Copy-paste these straight into v0's chat to customize the Third-Person Startertemplate. Each one names this template's own components — no generic filler.
Replace placeholder box with a GLTF character model
Swaps the placeholder box with a real 3D character model loaded from /public/models/, maintaining the existing movement and camera logic.
Replace the placeholder PlayerCharacter box mesh with a GLTF model loaded from /public/models/character.glb using the useGLTF hook from @react-three/drei. Place the <primitive object={scene} /> at the same world position as the previous placeholder. Wrap the useGLTF call in a React Suspense boundary with a visible fallback box mesh so the scene doesn't go blank while loading. Update MovementController to drive the model's position via the same ref-based approach as before.Change scene environment and lighting preset
Gives the scene a night atmosphere with targeted lamppost lighting and a visible grid floor for development reference.
Replace the current SceneEnvironment preset with 'night' using the Environment component from @react-three/drei. Change the AmbientLight intensity to 0.3 and add a PointLight at position [0, 5, 0] with intensity 1.5 and color #FFD700 to simulate a lamppost. Add a THREE.GridHelper at y=0 to replace the plain GroundPlane texture, making it easier to see movement on the ground.
Add running animations from GLTF animation clips
Wires GLTF animation clips to the AnimationStateMachine so the character visually walks, runs, or idles based on player input.
Use the useAnimations hook from @react-three/drei to play animation clips from the loaded character.glb file. Define three animation clip names: 'Idle', 'Walk', 'Run'. In MovementController, read the current input velocity magnitude: no input → play the Idle clip, WASD pressed at normal speed → play Walk, WASD + Shift held → play Run. Use action.fadeIn(0.2) when switching clips for smooth transitions, and action.fadeOut(0.2) on the exiting clip. Drive this via the AnimationStateMachine state.
Add jump mechanic with gravity in MovementController
Adds a physics-based jump arc with gravity to MovementController, making the character feel grounded and responsive.
Add jumping to MovementController. Create a velocityY ref (starts at 0). When the player presses Space, check if the player is on the ground (y <= 0.01): if so, set velocityY.current = 8 to launch the jump. Each frame in useFrame, subtract gravity: velocityY.current -= 9.8 * delta. Add the velocityY to the player's Y position each frame. Clamp the player Y position to 0 (ground level) and reset velocityY.current to 0 on landing. Show a brief dust particle effect on GroundPlane at the landing position using a small Three.js Points cloud.
Add Supabase shared world position for multiplayer preview
Adds real-time multiplayer position sharing so players can see each other moving in the same 3D world — a working shared world preview using Supabase Realtime.
Add basic shared world state using Supabase. Every 500ms, call a Server Action that upserts the current player's position { user_id: string, x: number, y: number, z: number } into a Supabase table named player_positions. Generate the user_id from a UUID stored in localStorage under the key 'player-session-id' on first visit. Subscribe to a Supabase Realtime channel named 'world' to receive other players' position updates. In the R3FCanvas scene, render each other player as a grey box mesh at their reported x/y/z position. Add RLS to player_positions: users can insert and update only their own row (user_id = auth.uid() or freely if using anon), and all users can select. Use NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY from the V0 Vars panel.Gotchas when you extend it
The failures people actually hit when they push this template past its defaults — and the exact fix for each.
Error: Failed to load "https://esm.sh/@react-three/fiber" in V0 preview — blank canvas or spinner that never resolvesWhy: React Three Fiber and Three.js are too large and complex for V0's esm.sh browser sandbox to reliably resolve. This is a known limitation of the preview environment, not a bug in your code.
Fix: Don't test 3D templates in V0 preview at all. Click Share → Publish → Publish to Production and test exclusively on the Vercel URL.
React Three Fiber fails to load in V0 preview. This is a known limitation with large 3D libraries and V0's esm.sh sandbox. Deploy with Share → Publish and test on the live Vercel URL.
ThirdPersonCamera clips through scene walls and geometry when the player is near obstaclesWhy: ThirdPersonCamera interpolates directly to its offset position behind the player without performing any collision check against scene geometry. No raycast occlusion avoidance is built into the base template.
Fix: Add a Three.js Raycaster from the player position toward the desired camera position each frame. If the ray hits geometry, move the camera to the hit point minus a small offset rather than the full orbit distance.
The third-person camera clips through walls. Add a Three.js Raycaster from the player position toward the camera target position. If the ray hits scene geometry, move the camera to the hit point minus a small offset. Use useFrame to update the raycast every frame.
Player moves faster on high-FPS monitors and slower on low-FPS devices — movement is frame-rate dependentWhy: MovementController adds raw unit values to the player position each frame without multiplying by delta time. At 120fps the player covers twice the distance per second compared to 60fps.
Fix: Multiply all movement vector components by the delta argument from useFrame's state object. useFrame provides delta as the second argument: useFrame((state, delta) => { ... }).
Player moves at different speeds depending on frame rate. In MovementController's useFrame callback, multiply all movement vector components by the delta argument: position.x += velocity.x * delta. Delta is provided as the second argument to useFrame: useFrame((state, delta) => { ... }).@react-three/rapier physics bodies fall through the GroundPlane — character sinks into the floorWhy: When adding Rapier physics, the GroundPlane mesh needs an explicit fixed RigidBody and a matching CuboidCollider. Without these, Rapier treats it as having no physics body and lets dynamic bodies pass through.
Fix: Wrap the GroundPlane mesh in a RigidBody with type='fixed' from @react-three/rapier and add a CuboidCollider with dimensions matching the plane size.
The player character falls through the ground when using Rapier physics. Wrap the GroundPlane mesh in <RigidBody type='fixed'> and add a <CuboidCollider args={[50, 0.1, 50]} position={[0, -0.1, 0]}> to provide a physics collision surface.Template vs. custom — the honest call
A forked template gets you far, fast. Here's where it holds up, and where you'll outgrow it.
The template is enough when
- You're prototyping a 3D game mechanic or interactive experience and need third-person controls working today
- You want to evaluate @react-three/fiber's capabilities with a working character controller before committing to the stack
- You're building a 3D product showcase or virtual showroom prototype where the character is secondary to the scene
- You're learning Three.js and react-three-fiber with a real working example that covers camera, movement, lighting, and GLTF loading
Go custom when
- You need a full game engine with physics simulation, AI pathfinding, and real-time networking — these require dedicated tooling well beyond what react-three-fiber alone provides
- You need mobile touchscreen controls — pointer lock doesn't work on mobile and requires a custom virtual joystick on the HUDOverlay
- You're targeting WebXR or VR — requires different camera rig, input handling, and XR session management not present in this template
RapidDev extends V0 3D starters into full interactive experiences — asset pipelines, physics, performance optimization, and production deployment.
Frequently asked questions
Is this template free to fork and use?
Yes. Forking v0.dev community templates is free with a v0.dev account. The fork consumes no credits. Deploying to Vercel is free on the Hobby plan. The libraries used — @react-three/fiber, @react-three/drei, Three.js, and Next.js — are all open-source (MIT licensed).
Can I use this template commercially?
Yes. You own the code output of your V0 fork and can use it in commercial products and client projects. Three.js, react-three-fiber, and all drei helpers are MIT licensed and permit commercial use without attribution requirements.
Why does my fork break in V0 preview — I see a blank canvas or loading spinner that never resolves?
This is expected behavior for 3D templates. V0's esm.sh preview sandbox cannot reliably load @react-three/fiber and Three.js because these libraries are too large for the browser-based sandboxed environment. The correct workflow for this template is: fork → publish immediately (Share → Publish → Publish to Production) → test only on the live Vercel URL. Do not attempt to debug 3D rendering in V0 preview.
What browsers support this template?
Desktop Chrome and Firefox provide the best WebGL 2 performance and full pointer lock support. Safari works but may show reduced frame rates on some hardware configurations. Mobile browsers are not recommended for the base template because pointer lock (used for camera control) doesn't work on touch devices — you'd need to add a custom virtual joystick to HUDOverlay.
How do I add my own 3D character model?
Place your .glb file at /public/models/character.glb and use the first prompt from the prompt pack above. It loads the model with useGLTF from @react-three/drei, wraps it in Suspense, and connects it to the existing MovementController. Free rigged GLTF characters with animations are available from Mixamo at mixamo.com.
How do I connect a database or add multiplayer?
Use the Supabase shared world prompt from the prompt pack above. It sets up a player_positions table with Supabase Realtime subscriptions so players can see each other move. Add NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY to the V0 Vars panel before publishing. Test the multiplayer feature on the Vercel URL, not in V0 preview.
Can RapidDev help me build a full 3D experience with this template as a starting point?
Yes. RapidDev extends V0 3D starters into full interactive experiences — adding asset pipelines, physics via Rapier, performance optimization, and production deployment. Visit rapidevelopers.com to discuss your 3D project.
Does this template support physics for collisions and jumping?
The base template has a minimal collision system for staying on the ground plane. For full physics (jumping, rigidbody collisions, character controller), install @react-three/rapier and use the jump prompt from the pack for gravity-based jumping. The Rapier gotcha in this page covers the common 'falls through ground' issue you'll hit when adding Rapier.
Outgrowing the template?
RapidDev turns v0 prototypes into production apps — real auth, database, and payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.