ondreu138 downloadsOpen and view STEP (.step/.stp) and STL, OBJ, CAD, FCStd models in an interactive 3D viewer.
View CAD models directly inside Obsidian in an interactive 3D viewer. Click a
supported file in the file explorer and it opens in a
three.js-powered viewer with orbit / pan / zoom, model
colours, edges, measurement, annotations and a navigation cube — or embed a
model inline in any note.
Supported formats:
.step / .stp) — full B-rep parsing with assembly hierarchy and
per-face colours..FCStd) — native FreeCAD documents; the visible objects are
rendered with their placements and colours.Geometry parsing is done with
occt-import-js (OpenCASCADE
compiled to WASM), which reads both STEP and the BREP shapes stored inside
FreeCAD documents.
Mobile support (beta). The plugin also runs on Obsidian mobile: the layout adapts to small touchscreens and orbit / pan / pinch-zoom work with touch (tap a part to see its info). Because the WASM parser can run out of memory on large models in the mobile webview, opening a large file first shows a warning you can dismiss to continue.




Install from the Obsidian Community Plugins browser (search for "STEP Viewer"), or install manually:
Download main.js, manifest.json and styles.css from the
latest release into
<vault>/.obsidian/plugins/step-viewer/, then enable the plugin.
.step / .stp,
.FCStd FreeCAD documents, and .obj / .stl triangle meshes..FCStd file is a ZIP of native BREP shapes; the
plugin unzips it, renders every object that is visible in FreeCAD, applies
each object's placement, and colours it from its FreeCAD shape colour.
Sketches, datums and hidden construction features are skipped..obj mesh.three.js: orbit / pan / zoom, per-face colours from
the model (default material otherwise), edge display, and auto-fit on load.Measurement and annotations are approximate. They run against the tessellated mesh, not the exact B-rep geometry, so readings are close to — but not exactly — the true CAD dimensions. The measurement readout is labelled
(approx.)accordingly.
Open any supported file from the file explorer. The toolbar (top-right, below the navigation cube) provides:
| Button | Action |
|---|---|
| Fit | Reset / fit the camera to the model |
| Wireframe | Toggle wireframe rendering |
| Edges | Toggle edge display |
| Transparency | Make surfaces translucent to see inside |
| Projection | Switch perspective ↔ orthographic |
| Section | Show the clipping-plane control (axis / flip / position) |
| Explode | Show the explode slider |
| Measure | Enable measuring; pick a type from the strip that appears |
| Snap | Snap measurement/annotation picks to corners & edges |
| Annotate | Click a point to pin an editable note |
| Screenshot | Save a PNG of the current view next to the model |
| Annotations | Open the annotations list panel |
| Structure | Open the assembly structure tree |
The buttons are split into separate cards by group (framing · appearance · inspect · measure/annotate · output/panels). The ↶ / ↷ arrows below the cube roll the view; clicking a cube face first undoes any roll so the view snaps to a clean, upright standard view.
Hovering a part in the model highlights it; clicking a part selects it (the highlight sticks, and its info card stays pinned) and reveals it in the structure tree — hovering never scrolls the tree. In the structure tree, hover a row to highlight that part in 3D, click it to select and frame it, and use the header controls to expand/collapse one level at a time, toggle all visibility, auto-measure the selection's bounding box, filter by name, or isolate the selection. Drag the panel's bottom-right grip to resize it.
```step
path: Models/bracket.step
height: 320
view: front # optional: front/back/left/right/top/bottom/iso
rotate: 90 # optional: initial roll in degrees (or `roll: 1` in quarter turns)
annotations: false # optional: hide saved notes in this embed (default true)
quality: detailed # optional: fastest/balanced/detailed, or a coarseness like 0.01
```
path accepts a wikilink target ([[bracket.step]]) or a vault-relative path;
height is optional (pixels, default 400). Each embed mounts its own viewer
when it scrolls into view and is disposed when it scrolls away, so a note with
many embeds doesn't exhaust the browser's limited WebGL contexts.
Annotations and pinned measurements are keyed by file path, so notes and measurements added in the full view also appear in embeds of the same model, and vice versa.
Some surfaces render as hollow "frames" (you can see through them). A few
STEP files describe faces the geometry kernel can't tessellate — the surface
comes back with no interior triangles, so it renders as just its outline (the
reader, occt-import-js, does no shape healing).
The plugin reconstructs these planar faces automatically: it caps the open boundary loops of the affected parts, preserving holes and cutouts, so they render solid. This is on by default (Settings → Reconstruct missing faces) and only ever touches parts that are otherwise invisible.
Reconstruction handles flat faces. For anything it can't rebuild (curved faces),
the plugin shows a notice listing the parts; re-export the file through a
healing tool — opening it in FreeCAD and exporting again (as STEP, or as
.obj / .stl, which this plugin reads) rebuilds all faces cleanly.
A FreeCAD model looks empty or is missing parts. Only objects that are
visible when the document was last saved in FreeCAD are rendered, and only
objects that carry solid/surface geometry (sketches and datums are skipped). Make
the parts you want visible in FreeCAD, save, and reopen. Assembly features that
rely on FreeCAD's own solver (e.g. some App::Link / assembly workbench setups)
may not carry standalone shapes; exporting such a document to STEP is the most
reliable route.
npm install
npm run build # type-checks and bundles a self-contained main.js
npm run dev # esbuild watch mode
The build inlines occt-import-js.wasm into main.js (gzipped, inflated at
runtime), so no separate WASM file ships.
Bump the version in manifest.json, package.json and versions.json, commit,
then push a matching tag (no v prefix):
git tag 1.13.0 && git push origin 1.13.0
.github/workflows/release.yml builds and publishes a GitHub release with the
plugin assets (main.js, manifest.json, styles.css).
Source lives in src/:
main.ts Plugin: registers view, extensions, embed processor
view/StepView.ts FileView: file lifecycle, owns the viewer
embed/StepEmbed.ts `step` code-block embed (lazy mount/dispose)
viewer/mountViewer.ts Builds the full viewer + UI (shared by view & embed)
viewer/OcctLoader.ts Lazy singleton over the inlined occt-import-js WASM
viewer/occt.worker.ts Off-main-thread STEP/BREP parse (Web Worker)
viewer/StepToThree.ts occt result JSON -> THREE.Group + structure tree
viewer/MeshLoaders.ts OBJ / STL parsing into the viewer model
viewer/FreeCadLoader.ts .FCStd unzip + Document/GuiDocument.xml + BREP shapes
viewer/GeometryCache.ts Off-vault (IndexedDB) parsed-geometry cache
viewer/HealFaces.ts Reconstruct untessellated planar faces
viewer/ViewerController.ts Scene, camera, controls, picking, measure, dispose
viewer/fitCamera.ts Camera fit to bounding box
viewer/params.ts Tessellation parameters by file size
ui/Toolbar.ts Toolbar buttons + measurement-type strip
ui/ViewCube.ts Navigation cube
ui/ViewControls.ts Floating section + explode controls
ui/TreePanel.ts Structure-tree panel (select / frame / isolate)
ui/PartInfoPanel.ts Hover info panel
ui/LabelLayer.ts Projected HTML labels + leader lines (screenshot draw)
ui/AnnotationLayer.ts Annotation pins + notes (markdown / link / colour)
ui/AnnotationsPanel.ts Annotations list panel (hide / opacity / filter)
ui/MeasurementLayer.ts Pinned (persistent) measurements
annotations/AnnotationStore.ts Per-file annotation persistence (plugin data)
annotations/MeasurementStore.ts Per-file pinned-measurement persistence
annotations/pluginData.ts Serialized read-modify-write of data.json
types.ts, occt-import-js.d.ts occt typings + result shape
Each open view (and embed) owns a WebGL context and disposes it on close
(renderer.dispose() + forceContextLoss()), which is essential to avoid
exhausting the browser's limited context pool.
MIT (this plugin). occt-import-js / OpenCASCADE is LGPL-2.1; the bundled
occt-import-js.wasm is distributed under that licence (see
node_modules/occt-import-js/dist/license.*.txt).