|
| 3D viewport | Orbit / pan / zoom (OrbitControls), presets (Front / Right / Top / Iso), one-click Fit. |
| Render modes | Visual, Collision, or Both — see Render modes → |
| Frames & inertia | Per-link TF axes (off / selected / all) and inertia ellipsoids + CoM markers. |
| Configurable up axis | +X, +Y, +Z — grid and camera adjust together. |
| Wireframe overlay | Spot cracks, inverted normals, high-poly collision meshes. |
🦾 Driving
| Joint sliders | Live sliders + numeric inputs honoring <limit> for revolute, continuous, prismatic. |
| Mimic propagation | <mimic> joints follow their master, with limit-clamp bypass so propagation isn't truncated. |
| Ignore limits | One-click bypass of every joint's <limit> for exploration. |
| Search & filter | Substring filter, only modified toggle. |
| Named states | SRDF <group_state> blocks appear in the bookmark dropdown — see Joints → |
🩺 Analysing
| Checks panel | Every parse error, missing mesh, malformed inertia, joint cycle. Catalog → |
| Link tree & inspector | Click anywhere on the robot or tree → see joints, mass, CoM, inertia tensor, mesh paths. |
| Diagnostics in VS Code | Same checks surface in the Problems panel with line numbers. |
🛠 Authoring
| Source editor (CodeMirror 6) | URDF/xacro syntax highlighting, live edit ↔ 3D preview (debounced ~160 ms), intelligent completion (link/joint/joint-type/package:// names), and inline lint markers. Toggle "Edit: on" from the Source tab. |
| Lint rule engine | 18 rules covering structural (R-001..005), physics (P-001..006), assets (A-001..003), and xacro style (S-001..005). Grouped Checks panel with health score (0-100). |
| Quick fixes | One-click fixes for missing <limit>, missing <inertial>, negative mass, zero effort/velocity, and stray limit on continuous joints. |
| Fullscreen source view | F11 (or Ctrl+Shift+F) flips the source pane to full width with the 3D viewport shrunk to a picture-in-picture corner — read tight URDF on a small screen. |
| 3D screenshot | Tools tab → Save PNG writes a transparent-background snapshot of the current viewport at 1×/2×/3×/4× scale; Copy to clipboard for instant paste into PRs/docs. |
| Reachability sampling | Monte-Carlo workspace point cloud for any tip link. |
| Never-colliding pairs | Sample for <disable_collisions> entries → write merged SRDF. |
| Pose & bookmarks | Save pose, name bookmarks, restore on next open. |
| Export | JSON pose with camera; PDF report bundling screenshot + checks + summary; BOM CSV. |
🤖 ROS / URDF / xacro
| xacro expansion | xacro:include, xacro:macro, xacro:arg, load_yaml, Python ternary / ** / slice rewrites. |
package:// URIs |
Auto-discovered from every package.xml in scope. |
| Mesh formats | STL · COLLADA · OBJ · glTF · GLB. DAE / GLTF external assets pre-resolved to blob URLs. |
| SRDF | Joint groups, named states, disable_collisions. |
Tested models
End-to-end Playwright smoke test against the upstream
franka_description
package. Reproducer:
git clone https://github.com/frankarobotics/franka_description /tmp/franka_description
npm run web:build
FRANKA_DIR=/tmp/franka_description node scripts/test-franka.mjs
| Robot | Source | Result |
|---|---|---|
Franka Research 3 (fr3) |
franka_description | ✅ 8 joints · 25 links · 0 errors · 0 warnings · ~600 ms |
Franka Research (fer) |
franka_description | ✅ 8 joints · 25 links · 0 errors · 0 warnings · ~800 ms |
Franka Production 3 (fp3) |
franka_description | ✅ 8 joints · 25 links · 0 errors · 0 warnings · ~600 ms |
The whole pipeline runs in the browser: directory pick, xacro expansion
(including load_yaml for joint limits / inertials YAMLs), package
resolution, mesh blob URL allocation, Three.js render.
Releases
Merges to main automatically select a stable version, run the complete test
suite against the candidate VSIX, publish it to the Marketplace, and create a
GitHub Release. No manual version bump is needed. develop and
design/quiet-studio pushes publish beta builds after the same checks.
Stable versions advance above any newer beta (for example, 0.5.x beta →
0.6.0 stable), so users on the beta channel can upgrade. Failed runs reuse their
release checkpoint when rerun. See release automation
for version rules, artifacts, and recovery.
Configuration
Both targets expose the same five settings.
| Setting | Default | Effect |
|---|---|---|
| Default render mode | visual |
Geometry layer on first load. |
| Up axis | +Z |
World up axis used by camera and grid. |
| Default xacro args | {} |
Args merged into every xacro file. |
| Extra package roots | [] |
Extra package.xml scan roots. |
| Semantic files | [] |
SRDF / YAML semantic files. |
Workspace: Camera presets and geometry layers sit inside the viewport; Display contains grids, frames, labels, and diagnostic overlays. Drag the inspector divider to resize it, or focus it and use the arrow keys. Joint controls pair full-width sliders with numeric values and units. Checks are grouped into collapsible sections with source links. Export collects PNG, pose JSON, parts CSV, and inspection PDF exports.
Colors: Classic colors in the Settings (gear) menu switches between the original palette and the new Studio palette in both the browser and VS Code. The choice is remembered independently of the light/dark theme.
Web: ⚙ button in the topbar → JSON in localStorage.
VS Code: urdfStudio.* keys in settings.json.
Details: docs/features/settings →
Architecture
A single TypeScript codebase produces both targets:
src/core/ — pure logic, no fs/path/DOM imports
▲
┌─────────────┴─────────────┐
io.node.ts ioBrowser.ts
(jsdom + node:fs) (FileSystemAccess + native DOM)
▲ ▲
src/extension.ts src/web/host.ts
▲ ▲
└─────────── postMessage ──────┘
▼
src/renderer/main.ts — Three.js + URDFLoader, identical
The core never touches Node-only modules directly; it queries a
CoreIo interface set by the host. The renderer is bundled separately
and is identical on both targets — the only thing that differs is the
postMessage source.
Deep dive: docs/architecture →
Local development
git clone https://github.com/deyuf/urdf-studio
cd urdf-studio
npm ci
Common loops:
# VS Code extension
npm run watch # incremental rebuild; press F5 in VS Code
# Web app
npm run web:dev # http://127.0.0.1:5173 with HMR
# Docs only
npm run docs:watch # rebuild dist-web/docs on every .md change
# Tests
npm run test:unit # core, editor, renderer logic, and browser storage
npm run test:renderer # builds both targets, then runs browser tests
npm run test:vscode # real VS Code integration (Linux: use xvfb-run)
# Real-world smoke
FRANKA_DIR=/tmp/franka_description node scripts/test-franka.mjs
Regenerate the PNG icon from its SVG master:
node scripts/build-icon.mjs
# Set CHROMIUM_PATH=/usr/bin/chromium to use an installed browser.
Refresh the README screenshots and animation (requires ffmpeg and a full
Franka mesh directory; the checked-in xacro fixture pins the model definition):
cp -a test/fixtures/franka_description /tmp/urdf-readme-franka
cp -a /path/to/franka_description/meshes /tmp/urdf-readme-franka/meshes
npm run web:build
FRANKA_DIR=/tmp/urdf-readme-franka node scripts/capture-screenshots.mjs
# Set CHROMIUM_PATH=/usr/bin/chromium to use an installed browser.
Production builds:
npm run package # VS Code extension (dist/)
npm run web:build # web app + docs (dist-web/)
npm run vsce:package # .vsix for sideload / Marketplace
# then: code --install-extension urdf-studio-*.vsix
More: docs/development/building →
Contributing & license
PRs welcome.
License: MIT.