Ecctrl: physics-driven character, vehicle and drone controllers for React Three Fiber
🕹️ A physics-driven controller toolkit for React Three Fiber + Rapier
At a glance
- What is it?
- Ecctrl is a controller toolkit for the React Three Fiber and Rapier stack, covering floating character controllers, per body gravity fields, torque driven cars, thrust driven drones, touch input and editable physics curves. It is a React library first, and it needs a recent enough three.js, react-three-fiber and rapier to work at all.
- Who is it for?
- Ecctrl fits a web game team that wants a character that reacts to physics rather than sliding along it, and that wants the same toolkit to cover cars, drones and touch input in one package, and it is the wrong choice for a project that is not React Three Fiber and Rapier, since the whole design assumes both.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 47 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A controller layer for a specific stack
Ecctrl is a physics-driven controller toolkit for React Three Fiber and Rapier, written in TypeScript and released under MIT by Erdong Chen. It sits in the pmndrs organisation, alongside the libraries it depends on, and its stated scope is character, vehicle, drone and custom gravity controllers.
The problem it addresses is the one every web game developer hits: the character controller. Most browser game physics either glues the character to the ground, which looks wrong on slopes, or lets them slide and clip through geometry, and the fix is usually a hand written raycast or shapecast loop that every project reimplements badly. Ecctrl's position is that a controlled object should behave like part of the physics world, and the mechanism it uses for that is a floating character held off surfaces by spring and damping rather than glued to them.
The version floor is the first thing to look at, because it is strict. The badges in the README state three.js 0.184 or newer, react-three-fiber 9.4 or newer, and react-three-rapier 2.2 or newer. That is a recent stack, and an existing project on older versions of any of the three has a decision to make before it can adopt this.
The package is at version 2.0.2 in the project file, there are no GitHub releases, and the last push to the repository was on 2026-09-06. The live demo at ecctrl.app is the recommended way to evaluate it, and it covers the character controller, custom gravity, cars, drones, touch controls, time control and the tuning tools.
Related work from the same author is BVHEcctrl, with its own demo site, which is worth knowing about if the vehicle side is where your interest lies.
The character floats, and pushes back
The core mechanism is a ShapeCast character controller, and its behaviour is the interesting part of the library.
The character floats with spring and damping. It pushes back on the object below it, it follows moving platforms, it aligns with the custom gravity direction, and it exposes runtime state for animation and gameplay logic. That last point is what makes it usable in a real game rather than a demo, because the animation system needs to know things like whether the character is grounded, which way it is facing and how fast it is moving, and reading that from your own physics queries is exactly the duplication the library is meant to remove.
The shape cast approach is also what gives the accuracy claims. The highlights table describes stable floating support, accurate standing points, gap handling, slope checks, and an optional RayCast mode. Stable floating support means the character is held at a small distance from the surface rather than intersecting it, which is what stops the jitter you get when a capsule is driven directly into geometry. Accurate standing points means the contact point is computed properly, so a character can stand on the edge of a platform and on slopes. Gap handling and slope checks are the two failure cases that separate a usable controller from a demo.
The RayCast mode is there for the cases where a shape cast is more work than a ray is, and it is optional rather than the default.
Custom gravity is applied per body, so the global vector goes to zero
Custom gravity is one of the main systems in Ecctrl, and the way it is implemented has a direct consequence for how you configure the physics world.
Instead of relying only on Rapier's global gravity vector, Ecctrl can read a gravity field function and apply gravity per body. That makes it possible to build spherical gravity, cylindrical gravity, wall-walking zones, gravity tunnels, rotating gravity fields, or gameplay-specific attraction areas. The gravity is described as position-based, and the table adds that the same field is shared by characters, vehicles, drones and custom rigid bodies, so a single definition governs everything in the scene rather than each body having its own private pull.
The configuration detail that trips people up is this: when Ecctrl applies custom gravity, you set Rapier's global gravity to [0, 0, 0] so the global gravity force and the custom gravity field do not stack. If you leave the default gravity in place, the two forces add and every body accelerates twice as fast as intended.
<Physics gravity={[0, 0, 0]}>
<Ecctrl enableCustomGravity>
<CharacterModel />
</Ecctrl>
</Physics>There is a dedicated useCustomGravity hook exported for the gravity module, and the example application in the repository has a GravityField file, so the pattern is demonstrated rather than described in the abstract.
Installing it and building the first scene
The package itself is one install:
npm install ecctrlThe peer dependencies are the rest of the stack:
npm install three react react-dom @react-three/fiber @react-three/rapierAnd two optional packages cover the conveniences:
npm install @react-three/drei levaThe README is specific about what those two are for. drei is only needed for helpers such as KeyboardControls and camera tooling, and if you do not use it you can still drive controller input through refs or stores and build your own camera follow logic. leva is only needed if you want the visual curve editor or the debug panels. So neither is a hard dependency, and the cost of skipping them is your own code rather than a missing feature.
The smallest useful character setup wires three things together: a keyboard map, the Physics provider from react-three-rapier, and the Ecctrl component. The map is an ordinary array of named actions and key codes:
const keyboardMap = [
{ name: "forward", keys: ["ArrowUp", "KeyW"] },
{ name: "backward", keys: ["ArrowDown", "KeyS"] },
{ name: "leftward", keys: ["ArrowLeft", "KeyA"] },
{ name: "rightward", keys: ["ArrowRight", "KeyD"] },
{ name: "jump", keys: ["Space"] },
{ name: "run", keys: ["Shift"] },
];and the scene nests them:
export function CharacterScene() {
return (
<Physics>
<KeyboardControls map={keyboardMap}>
<Ecctrl>
<CharacterModel />
</Ecctrl>
</KeyboardControls>
</Physics>
);
}The CharacterModel is yours to provide, and the example directory in the repository has both a capsule character model in TypeScript and an animated character model, so there is a working starting point to copy rather than a blank page.
Subpath exports decide what ends up in your bundle
Ecctrl is split into subpath exports so you can import only the parts you need, and the set of entry points maps directly onto the feature list.
import { Ecctrl } from "ecctrl";
import { EcctrlVehicle, ShapeCastWheel, ThrustPropeller } from "ecctrl/vehicle";
import { Joystick, VirtualButton } from "ecctrl/input";
import { useCustomGravity } from "ecctrl/gravity";
import { EcctrlCameraControls } from "ecctrl/camera";
import { TimeControl } from "ecctrl/time";
import { EcctrlAnimationStateController } from "ecctrl/animation";
import { bakeCurveLUT, evaluateCurveLUT } from "ecctrl/curves";
import { CurveEditorPlugin } from "ecctrl/leva";There is also an ecctrl/all entry point, and the README recommends it for prototypes and examples rather than for shipped code. The package file confirms the structure, with each subpath carrying its own types, commonjs and module builds, and the root export doing the same for the main entry point.
The vehicle module bundles three things that belong together. EcctrlVehicle is the vehicle, ShapeCastWheel is a wheel that is simulated with torque rather than faked with a rolling cylinder, and ThrustPropeller is the propeller used by the drone controllers.
The camera and animation modules are the ones people underestimate. EcctrlCameraControls is the follow camera, and EcctrlAnimationStateController is the piece that turns the physics state into animation state, which is the bridge between the controller and whatever animation system the project uses.
Cars, drones, curves and bullet time
The rest of the toolkit covers the vehicle and drone cases, and the tuning tools that make them usable.
The ShapeCast wheels give torque-driven wheel simulation with editable longitudinal and lateral slip curves, which is the part that makes a car feel like it is gripping rather than sliding on rails. The propeller drones mix thrust and reaction torque per propeller, which is what allows stabilised velocity or position flight rather than a drone that simply flies up. Curve LUTs are described as runtime-cheap curve sampling for grip, engine torque, steering and mass falloff, and the bakeCurveLUT and evaluateCurveLUT functions are the API for it. The optional Leva curve editor is a draggable editor for tuning points, tangents and weights, and it is the reason leva is in the optional dependency list.
Time control pauses and slows the physics world for bullet-time style control, and the touch controls are DOM-based joystick and virtual buttons that you can replace with your own UI, which matters because the default look is rarely the one a game wants.
The design pattern running through all of it is that the physics values are exposed rather than hidden. The character controller exposes runtime state, the vehicle exposes slip curves, the gravity system exposes a field function, and the curve system exposes baked lookup tables. That is what lets a project replace one piece, for example swapping the DOM joystick for a custom on-screen control, without giving up the rest.
The detailed documentation, with the full prop tables, configuration defaults, handle output, input shapes and tuning notes, lives in the API reference document in the docs directory, and the release history and upcoming changes are in the changelog.
Where this stops being the right answer
The limits are mostly the stack, and they are worth saying plainly.
Ecctrl is a React Three Fiber and Rapier library. It assumes a declarative React scene, a Rapier physics world, and a three.js version of at least 0.184. A project on a different renderer, a different physics engine, or a canvas game written without React gets nothing from it, because the value is in the components and the React state flow rather than in a portable algorithm. The version floor has the same character: an existing app on react-three-fiber 8 cannot install this without a migration.
The optional dependencies are a second limit, and a softer one. Skipping drei means writing your own input plumbing and camera follow, and skipping leva means no visual curve editor, so a team that tunes grip and torque curves by dragging points in a browser pays for its decision in a different way.
The alternative to the toolkit is writing the controller yourself, which for a ShapeCast character means a per frame ray or shape cast against the physics world, a spring and damper applied to the resulting offset, ground and slope detection, and the state plumbing to feed the animation system. That is the code Ecctrl contains, and the honest framing is that you would be writing a subset of it, badly, once. Writing it yourself is the right answer when your game needs a controller this toolkit does not model, or when a dependency on the React stack is not acceptable.
A third option is to keep the physics engine's own primitives and use a much simpler kinematic approach, which is less accurate on slopes and edges but is a fraction of the work for a game where the character never leaves flat ground.
Editorial conclusion
Ecctrl fits a web game team that wants a character that reacts to physics rather than sliding along it, and that wants the same toolkit to cover cars, drones and touch input in one package, and it is the wrong choice for a project that is not React Three Fiber and Rapier, since the whole design assumes both. Verify first the version floor, because the package expects three.js 0.184 or newer, react-three-fiber 9.4 or newer and rapier 2.2 or newer, and decide early whether you will take the optional dependencies, since without drei and leva you lose the keyboard helper and the visual curve editor and have to supply your own.
Frequently asked questions
What are the peer dependencies for Ecctrl?
The package expects three.js 0.184 or newer, react-three-fiber 9.4 or newer, and react-three-rapier 2.2 or newer, installed alongside three, react and react-dom. The optional packages are @react-three/drei for helpers such as KeyboardControls and camera tooling, and leva for the visual curve editor and debug panels.
How do I set up custom gravity with Ecctrl?
Set Rapier's global gravity to [0, 0, 0] so the global force and the custom gravity field do not stack, then enable the custom gravity mode on the controller with the enableCustomGravity prop. The gravity field function is applied per body, so characters, vehicles, drones and custom rigid bodies can share one field.
Can I import only part of Ecctrl?
Yes, the package is split into subpath exports: ecctrl/vehicle, ecctrl/input, ecctrl/gravity, ecctrl/camera, ecctrl/time, ecctrl/animation, ecctrl/curves and ecctrl/leva, plus an ecctrl/all entry point that the README recommends for prototypes and examples rather than shipped code.
What does the ShapeCast character controller do differently?
The character floats with spring and damping instead of being glued to the ground, and it pushes back on the object below, follows moving platforms, aligns with the custom gravity direction and exposes runtime state for animation and gameplay logic. The highlights list stable floating support, accurate standing points, gap handling, slope checks and an optional RayCast mode.
What licence is Ecctrl released under and how is it maintained?
MIT, with the licence text in the LICENSE file and a NOTICE file alongside it. The project file lists version 2.0.2, the repository publishes no GitHub releases, and the last push was on 2026-09-06, with the release history kept in the CHANGELOG.md.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pmndrs-ecctrl)Was this useful?