Put any FTC robot's CAD and its real Java code into a browser, and watch that robot run that code. It runs on the 2026-27 BIOBUZZ field, with its own mass, motors and traction, and the math behind every number.
Live: https://ftc-simbench-pro.pages.dev · rebuilt from main on every push
GearGurus 7832's 2024-25 robot, from the team's own Onshape export, running the team's own
sample_teleop.java. Gamepad 2's Y raised the four-stage lift 0.69 m and swung the outtake arm to
the basket. A pushed the intake out 226 mm through its servo linkage and dropped the claw. Nothing
here was re-coded for the simulator.
FTC teams write most of their code before the robot is finished, and then test it on a robot that is also being rebuilt. Existing simulators want the robot modelled again inside the simulator. Teams don't have time for that, and the model drifts from the real robot.
SimBench Pro starts from what a team already has:
- The CAD they built the robot from. From Onshape that is one step: paste the assembly's address. SimBench reads the assembly from Onshape's own API, signed in as the team: every mate becomes an exact joint with its limits and relations, parts fastened together collapse into rigid links, every part keeps its colour, and the mass, centre of mass and inertia come from Onshape's own mass properties. Nothing is exported or uploaded. The export zip (Export → URDF) still works where the link can't, Fusion, SolidWorks and FreeCAD teams drop a URDF zip (tools/fusion writes one from Fusion), and a plain STEP still works, with the joints found from the geometry.
- The OpModes they already run on the Control Hub: TeleOps and Road Runner autos, as
.java.
From those two things alone it works out the robot:
- Frame and drivetrain: where up is, where the drivetrain centre is, and what the drivetrain is (mecanum, tank, swerve, kiwi, X-drive).
- Mass: weight, centre of mass and inertia.
- Actuators: which motors and servos drive what.
- Joints: how every mechanism moves.
It then runs the code at 50 Hz against a rigid-body model on the season's field. You drive it with a real gamepad and see what the code does, before the robot exists or while it's in pieces.
The hard part is joints. A STEP file has shapes and positions, but no hinges and no slides. Getting from "773 parts" to "this lift, this arm, this claw, moving this way" is most of the project. There are four layers, from exact to automatic:
| Layer | What it is | Status |
|---|---|---|
| The Onshape link | The main way in. A team pastes its assembly's address; src/onshapelink.js reads the assembly definition (every mate, with its frame on each side), its features (limits, gear and rack relations) and, once per Part Studio, each part's tessellated faces and mass properties, through the Sign in with Onshape relay (functions/onshape/), in the engine worker. src/onshapecad.js makes the robot: one thinned shape per unique part, drawn as instances; src/mates.js collapses fastened parts into rigid links and walks the moving mates out as the joint tree; the links' mass properties are Onshape's own, summed. Works on school Chromebooks. |
✅ src/onshapelink.js, src/onshapecad.js, src/mates.js |
| The export zip | Where the link can't be used: Onshape writes the assembly's mates as URDF joints, its mass properties as inertials, and every part as a GLB mesh in one zip. src/meshfiles.js unzips and reads GLB, glTF, OBJ and STL on the page; src/urdf.js turns it into the same robot. The relations URDF can't carry are inferred as hints and applied only when the code confirms them. |
✅ src/meshfiles.js, src/urdf.js |
| Which joints are mechanisms, and what drives them | A library assembly carries a revolute inside every motor and bearing; exported, a real robot had 113. src/bind.js marks those internal from what they carry, then ties the code's devices to the real joints by name, by the actuator kind on each joint's axis, by the travel the code asks for, and by elimination. Only a device with two live candidates is a question. |
✅ src/bind.js |
| Exact joints | Which of an export's joints are mechanisms, and what drives each, said once: in the mates' own names in Onshape (motor armMotor, servo claw, follow liftL x2) or in the joint sheet on the page. With a declaration, every other joint is held and no device is bound by guessing. |
🧪 src/jointsheet.js, docs/exact-joints.md |
| Joint spec | A small JSON file that says the same by hand: parts, axis, pivot, travel, which device drives it. It also covers cascade slides and servo slider-crank linkages. | ✅ src/jointspec.js, docs/joints.md |
| Click-to-fix editor | In the CAD view: click parts, pick what they ride on, make a joint (the axis is suggested from the selected spline, gear or rail), flip it, try it. Every edit is a joint spec you can download. | ✅ src/cadview.js |
| Automatic joint finder | Finds actuators, slide stacks and the parts each joint carries, straight from geometry, for any STEP. Runs by itself when a robot has no mates and no spec, and lists what a person should check. | ✅ src/autorig.js, from the measured prototypes in research/autorig |
| The match | An alliance partner and two opponents the bench drives, and both alliances' human players, playing AUTO or TELEOP by the manual's rules, sharing the HIVEs with the team's robot, with a live scoreboard. | ✅ src/match.js, docs/match.md |
| The import card | One card over the field says the one thing to do next: bring the robot (paste the Onshape link, or drop the export), add the code, then a review of what the bench worked out (up, front, drive base, mass, joints, device bindings) with the few questions only the team can answer, each with a "show me" that moves the part. Reading happens in a Web Worker, so the page never freezes. | ✅ src/importflow.js, src/engineworker.js, docs/robot-setup.md |
| Online matches | One match with other teams, each on their own computer with their own robot and code: Quick match, a room code or an invite link, alliance chat, marks on the field. Browsers connect directly; the host's bench keeps the score. | ✅ src/net.js, docs/online.md |
| Robot check | Checks the joints, wherever they came from, against the team's own OpMode: every motor and servo it moves has a joint, every joint carries parts and is driven, nothing swings through the frame. What it can't confirm becomes a question in the team's device names, with the likely answers and a button to see each one move. | ✅ src/robotcheck.js, docs/robot-check.md |
The goal is that every robot ends up right: exact from Onshape mates when there are any, and otherwise checked against the team's code, with a few questions only that team can answer. Nothing wrong is shown without saying so.
| Any robot's export | meshfiles.js reads the zip Onshape (or a Fusion, SolidWorks or FreeCAD exporter) writes: the URDF and its GLB, glTF, OBJ or STL meshes, all on the page. urdf.js makes the robot from it, and bind.js settles which joints are mechanisms and which device drives each. step.js resolves a plain STEP's assembly tree. frame.js puts every robot in one frame (+z up, origin at the drivetrain centre on the floor). drivetrain.js finds the wheels, the drivetrain type and each motor's mounting. inertia.js works out mass, centre of mass and inertia from part shapes and vendor data. Tested on 14 generated robots of every drivetrain type and on a real 773-part robot. |
| The robot as Onshape draws it | tessellate.js meshes every real surface with OpenCascade in Web Workers, one small STEP per unique shape. The real robot has 99 shapes, placed 773 times, in about 12 s. view3d.js draws them with realistic materials; the wheels spin with their motors. cadview.js is an Onshape-style CAD view with a view cube, instance tree, part picking and mass properties. |
| Real code, unmodified | java.js + expr.js interpret LinearOpMode/OpMode TeleOps and autos: hardware maps, directions, encoders, RUN_TO_POSITION, FTCLib PID (with its real integral bounds), timers, sleeps, switch/enum state machines. Gamepads map to gamepad1/2, with rumble. |
| Road Runner 1.0 autos | roadrunner.js reads TrajectoryActionBuilder chains, Actions.runBlocking trees, and the team's own Action classes from their helper files (an Arm class, a PID class, MecanumDrive and its PARAMS). It builds the paths, time-profiles them, and follows them with the team's gains. See docs/code-support.md. |
| Rigid-body physics | dynamics.js models motor torque curves, per-wheel normal loads with load transfer, and slip-limited friction. Motor direction follows the FTC SDK: FORWARD turns the shaft clockwise seen from the shaft end, and the CAD says how each motor is mounted. |
| Checks | analyze.js reads the code against the robot: devices mapped or not, servos that stall, sleeps that freeze a TeleOp, slides the code never powers, a drive probe that says whether the sticks drive this robot the way a driver expects, and a lookup with a stray space in its config name. |
| Show the math | mathdoc.js prints the equations for your robot with your numbers in them (gear ratios, holding torque, traction limits, feedforward) and exports Markdown for an Engineering Portfolio. |
| BIOBUZZ field | The measured 2026-27 field from the BIOBUZZ Shot Sim, vendored in vendor/: HIVEs, CELLs, FLOWERs, POLLEN and NECTAR, and a shot model. |
| Workspaces | session.js saves the parsed robot, its joints, the code and the pose as a .ftcsim file; opening it needs no STEP re-parse. OpModes can also be imported straight from a GitHub repo. |
Open https://ftc-simbench-pro.pages.dev. It loads GearGurus 7832's Into The Deep robot
(assets/robots/into-the-deep) with the team's sample tele.
- START, then gamepad 2 (or the keys shown on the page):
- Y: high basket (lift up, arm back)
- X: hand-off pose
- A: intake out, arm down
- B: intake back in
- bumpers: claws
- Pick The Holy Grail under Autonomous to run the team's Road Runner specimen auto. It was written for the Into The Deep field, so it runs against the walls only.
- The CAD button opens the CAD view. Click a part, and its panel shows the joint it rides on, the joint's axis, and a slider to try it.
- Bring your own robot: open the assembly in Onshape, copy the address bar, paste it on the
card over the field and click Get my robot (Onshape asks once to let SimBench read your
documents). Then drop your
.javaOpModes and helper classes (or a zip of the TeamCode folder, or paste the GitHub repo). The card says what, if anything, is left to answer. The export zip (Export → URDF), a.stepor a saved.ftcsimworkspace drop in the same way. The Sim-ready CAD standard is in docs/robot-setup.md. ?robot=sampleopens the small built-in sample instead.- Online in the top bar plays one match with other teams, each on their own computer. Use Quick match, or host and send the invite link (docs/online.md).
| The joint editor | The team's Road Runner auto |
|---|---|
![]() |
![]() |
Onshape link ─► onshapelink.js ─► onshapecad.js ─┐ (in a Web Worker:
(the API, assembly, mates, shapes, links, │ engineworker.js)
signed in) features, masses mates.js joints ├──► frame.js ──► drivetrain.js · inertia.js ──► bind.js ──┐
URDF zip ─────► meshfiles.js ─► urdf.js ───────────┤ +z up, wheels, drive type, which joints │
STEP file ────► step.js ───────────────────────────┘ origin at motor mounting, move, which │
parts, assembly tree, drive centre mass & inertia device drives │
the geometry's joints (autorig.js) each ▼
.java OpModes ──► java.js · jvm.js ──► robotcheck.js ────────────────────────────────► sim.js ──► dynamics.js
+ helper files the team's code, what only the team can answer 50 Hz rigid body,
run as written (importflow.js asks, once) Driver wheels, slip
Station on field.js
│
tessellate.js (OpenCascade) ──► view3d.js (three r186, GTAO) · cadview.js ◄┘
analyze.js · mathdoc.js ──► Checks, Math, Graph tabs
Every src/*.js is a plain script fragment. tools/build.mjs concatenates them into one page;
there's no bundler and no framework. Only tessellate.js, view3d.js, cadview.js,
engineworker.js, importflow.js and app.js touch the browser. Everything else is the
engine, which tests/load.mjs loads into Node exactly as it ships, so the whole simulator is
tested without a browser; the build also ships the engine alone as dist/engine-<hash>.js, which
the page runs in a Web Worker to read CAD without freezing. The module map is in
src/README.md.
| Path | What's there |
|---|---|
src/ |
The app: engine modules, the 3D and CAD views, the UI. |
tests/ |
628 node:test tests: the robot corpus, physics, parser, Road Runner, the real robot end to end, the match, online play, the Onshape link and the export zip, exact joints and the device binding. |
tools/ |
The build, the minifier, the test runner, the robot corpus generator, and the Fusion Export to SimBench script. |
assets/robots/ |
The default robot: GearGurus 7832's STEP (gzipped), its joint spec, and the team's OpModes. |
research/autorig/ |
The automatic joint finder study: three prototypes, measured against the real robot. |
docs/ |
Guides (joints, code support) and the screenshots. |
vendor/biobuzz-shot-sim/ |
The BIOBUZZ field and shot engine (MIT, ours), synced by tools/sync-shot-sim.mjs. |
AGENTS.md |
How the two coding agents on this repo work together: rules, file claims, shared conventions. |
DEPLOY.md |
Hosting on Cloudflare Pages, and registering the Onshape app the link import needs. |
npm install # dev dependency only: occt-import-js, for the geometry tests
npm run build # dev build -> dist/index.html (open it, or serve dist/)
npm test # the whole suite: 628 tests
npm run build:ship # what Cloudflare Pages builds: comments and layout strippeddist/ is build output and isn't committed. Cloudflare Pages builds it on every push to main;
check the live build by comparing its SIMBENCH_BUILD hash with a local ship build.
- The physics is a model, not a measurement. Mass comes from CAD shapes and material density, or from a vendor figure when a part is recognised. Every number in the Math tab says where it came from. Check a real robot on a real field before you bet a match on it.
- Guessed joints need a person. On real teams' CAD the finder alone makes mistakes, which is why the robot check asks about anything it can't confirm. Onshape mates skip the guessing.
- Automatic joints are a first draft. The finder is measured on one real robot so far (see research/autorig). On it, it finds every joint, and every part it moves really moves, but 13 of 171 moving parts stay on the frame, mostly where the CAD itself is drawn wrong. The panel lists what to check, and the joint editor fixes the rest.
- Road Runner is emulated, not run. Paths, timing, markers and every action are the team's.
The follower uses the team's gains, but its feedforward is the bench's own, taken from the CAD's
motors and wheels, and the localizer is perfect. Tuned
kS/kV/kAvalues belong to one real robot's encoders and battery. - The Onshape link runs on a yearly allowance. Until the app is listed in Onshape's App Store, every read counts against its owner's 2,500 API calls a year, and a big robot's first read is a couple of hundred. When it runs out, the card says so and the export zip (no API at all) takes over. See DEPLOY.md, "Quota".
- Shipped code can be read. The ship build strips comments, which is not encryption. Nothing secret belongs in a browser bundle.
The repository is public so it can be read, but it is not open source: all rights reserved, see LICENSE. The free, MIT-licensed bench it grew from is LILRINO71/ftc-sim-bench.
- The default robot and its code are GearGurus 7832's, published at the team's request. The code is BSD-3-Clause-Clear, credited in its folder.
- OpenCascade meshing is occt-import-js (LGPL-2.1), loaded from jsDelivr at run time.
- 3D is three.js r186 (MIT), loaded as an ES module from jsDelivr with its GTAO, SMAA and RoomEnvironment add-ons.
- Online matches connect through Trystero (MIT), loaded from jsDelivr only when a player goes online.


