a-character-ctrlr
v0.1.0
Published
A React Three Fiber third-person controller library with a primitive player, follow camera, and ragdoll debugging sandbox.
Maintainers
Readme
CharacterCtrlr
CharacterCtrlr is a hybrid library + demo project for a React Three Fiber third-person controller stack. It ships a reusable primitive player, a follow camera with camera-occlusion handling, scoped controller state, a humanoid ragdoll dummy, and an in-world ragdoll debug lab.
Production active-ragdoll direction
The repo is now explicitly being pushed toward a production-capable active ragdoll for mobile and PC.
Important boundaries:
src/libis the shipping codepathsrc/componentsis demo-only support code- the demo is used to inspect and reproduce behavior, not to host one-off controller logic
- visual debugging is treated as part of the runtime engineering surface, not as optional polish
The chosen controller family is:
- deterministic
SIMBICON-style gait control capture-pointstepping heuristics for balance recovery- later directional locomotion expansion informed by
Generalized Biped Walking Control
See:
Production baseline
The library is now in a solid packageable baseline for:
- third-person movement with
idle,walk,run,crouch,jump, andfall - external or keyboard-driven input
- follow-camera control with pointer lock and scene-occlusion avoidance
- state snapshots and movement lifecycle callbacks
- a separate humanoid ragdoll test dummy with in-world debug visualization
What is not finished yet:
- step-up and slope-specialized controller handling
- tuned production active-ragdoll walking, running, and recovery behavior
- authored interaction systems for doors, vehicles, weapons, and golf
- skinned-character animation or motion-matching backends
Install
npm install a-character-ctrlr react react-dom three @react-three/fiber @react-three/rapier @react-three/dreiQuick start
import { Canvas } from "@react-three/fiber";
import { Physics } from "@react-three/rapier";
import { CharacterCtrlrCameraRig, CharacterCtrlrPlayer, CharacterCtrlrProvider } from "a-character-ctrlr";
export function Scene() {
return (
<CharacterCtrlrProvider initialState={{ playerPosition: [0, 2.5, 6] }}>
<Canvas shadows camera={{ fov: 42, near: 0.1, far: 250, position: [0, 3.5, 8] }}>
<Physics gravity={[0, -9.81, 0]}>
<CharacterCtrlrPlayer controls="keyboard" position={[0, 2.5, 6]} />
</Physics>
<CharacterCtrlrCameraRig />
</Canvas>
</CharacterCtrlrProvider>
);
}Controlled input example
Use controls="none" when you want to drive the player from your own touch, gamepad, AI, or network state.
import { useEffect } from "react";
import { Canvas } from "@react-three/fiber";
import { Physics } from "@react-three/rapier";
import {
CharacterCtrlrCameraRig,
CharacterCtrlrPlayer,
CharacterCtrlrProvider,
useCharacterCtrlrInputController,
} from "a-character-ctrlr";
function BotDriver() {
const controller = useCharacterCtrlrInputController();
useEffect(() => {
controller.replaceInput({ forward: true, run: true });
return () => controller.resetInput();
}, [controller]);
return (
<>
<CharacterCtrlrPlayer
controls="none"
inputRef={controller.inputRef}
onGroundedChange={(grounded) => {
if (!grounded) {
controller.pressInput("jump", false);
}
}}
/>
<CharacterCtrlrCameraRig />
</>
);
}
export function Scene() {
return (
<CharacterCtrlrProvider>
<Canvas>
<Physics>
<BotDriver />
</Physics>
</Canvas>
</CharacterCtrlrProvider>
);
}Exported API
CharacterCtrlrProvidercreateCharacterCtrlrStoreuseCharacterCtrlrStoreuseCharacterCtrlrStoreApiuseCharacterCtrlrKeyboardInputuseCharacterCtrlrInputControllerCharacterCtrlrActiveRagdollPlayerexperimentalCharacterCtrlrPlayerCharacterCtrlrCameraRigCharacterCtrlrRagdollDummyDEFAULT_CHARACTER_CTRLR_INPUTmergeCharacterCtrlrInput
Useful exported types:
CharacterCtrlrControllerStateCharacterCtrlrStoreApiCharacterCtrlrStoreInitCharacterCtrlrInputStateCharacterCtrlrMovementModeCharacterCtrlrPlayerSnapshotCharacterCtrlrSupportStateCharacterCtrlrVec3
Key component props
CharacterCtrlrPlayer supports:
controls="keyboard" | "none"inputandinputReffor additive external input- tunables for
walkSpeed,runSpeed,crouchSpeed,jumpVelocity,acceleration,deceleration, andairControl onSnapshotChange,onMovementModeChange,onGroundedChange,onJump, andonLanddebugfor the in-world player debug overlay- emitted snapshots currently report
supportStateas a simple"double"or"none"fallback for the capsule baseline
CharacterCtrlrActiveRagdollPlayer supports:
- the same input and lifecycle callback shape as
CharacterCtrlrPlayer - optional
mixamoSourceloading for hidden animation-target retargeting into the active ragdoll joint motors - experimental tunables for
jumpImpulse,uprightTorque,turnTorque, andbalanceDamping - experimental camera-target tuning with
cameraFocusSmoothing,cameraFocusHeight, andcameraFocusLead debugto view the articulated rig through the ragdoll debug overlay- emitted snapshots report articulated foot support as
"none","left","right", or"double"
Production note:
- active-ragdoll work is still marked experimental, but the implementation now has the intended long-term control architecture in place:
- explicit gait FSM
- phase-based pose targets
- COM and capture-point feedback
- step length, width, and clearance targets
- locomotion family configs
- recovery and deterministic gait re-entry
- grounded-state recovery now uses:
- contact hysteresis for grounded/airborne transitions
- delayed jump-contact clearing
- downward Rapier ground probes under the feet as a fallback when foot contact callbacks are unreliable
- standing is now handled as a dedicated support problem rather than just an idle gait:
- pelvis support is solved relative to the foot sole plane
- idle and low-speed double-support use a stand-assist path
- feet are planted and leveled while standing so the rig can hold posture before stepping
- new locomotion, balance, and recovery work should land in the library runtime first and only then be exposed through the demo
CharacterCtrlrCameraRig supports:
followOffset,focusHeight, andsmoothingpointerLock,yawSensitivity, andpitchSensitivitycollisionEnabled,collisionPadding, andminCollisionDistance
CharacterCtrlrRagdollDummy supports:
positiondebug- paused/manual-step demo debugging props for the in-repo sandbox
Repo layout
src/lib: publishable library sourcesrc/components: demo-only scene piecesdist: library build outputdemo-dist: demo build outputRESEARCH.md: academic basis for the active-ragdoll architectureROADMAP.md: production implementation plan and phase breakdown
Scripts
npm run dev: run the demo locallynpm run dev:lan: run the demo on your LANnpm run test: run the library smoke testsnpm run build:lib: build the package intodistnpm run build:demo: build the demo intodemo-distnpm run build: typecheck, test, and build both library and demonpm run preview:lan: preview the demo build on your LAN
Demo tip:
- the demo now defaults to the active ragdoll player
- add
?player=capsuleto the dev URL to load the capsule baseline instead - add
?motion=mixamoto enable the optional Mixamo target path in the ragdoll demo once the FBX files are placed inpublic/mixamo - the current ragdoll debugging/tuning path is
http://localhost:5173/?player=ragdoll&motion=mixamo
Mixamo tip:
- see docs/MIXAMO.md for the expected FBX downloads, file layout, and
mixamoSourceusage
Design direction
The current shipping strategy is deliberate: keep control, camera, state, and interaction logic deterministic first, then add more sophisticated animation systems later. For the active ragdoll specifically, that now means a production-focused finite-state controller with explicit support, foot planting, COM, capture-point, and balance diagnostics before any learned or mocap-heavy runtime is considered.
See also:
