Skip to main content
Gray Tsao

Orthoshot: ten standard views of a part, in one drag and drop

When a quote needs part views, the person who needs them usually has no CAD licence. This is a browser tool that opens STEP, measures it, estimates its weight and exports ten standard views in one click — with nothing uploaded, because the geometry belongs to someone else.

The person who needs the views has no CAD

Quoting, work orders, talking to a supplier — each one needs the same bundle: standard views of a part, plus its dimensions and weight.

Producing that bundle looks like this. Open CAD, square the model to the front view, screenshot, rotate to the back, screenshot, then left, right, top, bottom, then four isometrics, then name all ten files, then zip them. A few minutes per part; eight parts on one quote is half an hour.

The harder problem is who needs the bundle: sales, purchasing, whoever is currently on the phone with the customer. They usually have no CAD licence, and they should not have to learn CAD to take ten screenshots. So the real workflow is "ask an engineer for the views" — and then wait.

That wait is what Orthoshot removes.

Drop it in, press one button

Drag a .step / .stp / .stl / .glb / .gltf anywhere onto the window — no need to aim at a target box — and press export.

What comes back is <model>_snapshots.zip holding ten PNGs:

  • six orthographic: front, back, left, right, top, bottom
  • four isometric: iso_front_right_top, iso_front_left_top, iso_back_right_top, iso_back_left_top

At 1024 or 2048, on white or transparent, and the run can be cancelled part way through.

On the same screen:

  • Dimensions — width (X) / height (Y) / depth (Z), true volume, bounding-box volume, vertex and face counts
  • Weight — 63 engineering material densities in seven groups (aluminium, stainless, copper and brass, steel, tool steel / titanium / nickel alloys, plastics, hard and brittle materials / ceramics), or type your own density
  • Units — mm / inch toggle at the top, remembered in localStorage; the overlay in the 3D view shows both at once
  • Cross-section — pick the X / Y / Z cutting axis, drag the plane along it, flip which side is kept; the cut face is filled rather than left as a hollow shell

Nothing is uploaded

No server, no account, no upload. STEP is parsed in the browser by occt-import-js (OpenCascade compiled to WASM), rendering goes through WebGL, and the zip is assembled client-side.

This is not about saving on hosting. Part geometry belongs to the customer, and a tool whose first step is "upload it to us" is simply a non-starter inside a lot of companies. Rather than explain how long files live on the server and who can read them, it is better for that conversation not to exist.

The costs, stated plainly:

  • the occt WASM is about 7.6 MB, downloaded the first time a STEP is opened
  • a WebGL2-capable browser is required; when none is available the page says why (most often a work machine with hardware acceleration disabled by policy) rather than showing a blank panel
  • the file-size ceiling is whatever that machine's memory allows, and "add another machine" is not an option

A screenshot is not a drawing

Those ten images are not grabbed from the screen.

The first reason is pose. People rotate the model constantly — they are inspecting a part, not composing a shot. If snapshots followed the live view, the same part would export ten differently-angled images depending on who did it, and they would stop being standard views. So capture always uses the model's original pose: a throwaway orthographic camera, framed from the bounding box, with near / far tightened using the eight bbox corners projected onto the view direction. Nothing the user does on screen changes the output.

The second reason cost more to learn: colour.

Every PNG the offscreen pass produced came out markedly darker than the framed view.

The reason is in three's render path. renderer.toneMapping and renderer.outputColorSpace are applied only when rendering to the canvas (or an XR target); for any other render target three forces NoToneMapping and the linear working colour space. So the offscreen pass wrote raw linear, un-tone-mapped pixels, and a PNG viewer reads those bytes as sRGB. Hence dark.

The fix is not to hand-roll a shader that inlines the colour-space conversion. I tried; it does not compile. three already prepends colorspace_pars_fragment to every non-raw material, so inlining it again is a duplicate function definition.

The fix is three's own OutputPass: a RawShaderMaterial that reads the renderer's current toneMapping, outputColorSpace and toneMappingExposure and applies the same chunks in the same order as the canvas pipeline. The PNG matching the view is not something I tuned into place — it is the same code doing the same work.

Two more details that only bite offscreen:

  • the render target has to request stencilBuffer explicitly (three defaults it to false), or the cross-section cap — which is drawn through a stencil test — vanishes entirely from the snapshots
  • the scene renders first to a half-float, 4× multisampled target to match the canvas, which is created with antialias: true, falling back to 8-bit where extensions like EXT_color_buffer_float are missing

The 83 lines of unit detection I eventually deleted

I started out believing occt-import-js handed back STEP coordinates untouched, with no unit conversion. So I wrote an 83-line module to read the length unit declared in the STEP header and scale the coordinates to millimetres.

The belief was wrong. occt converts geometry to whatever its linearUnit parameter says, and that parameter defaults to millimetres. I was multiplying numbers that were already millimetres by a conversion factor a second time. Onshape exports in metres by default — so those files came out a thousand times too large.

Unwinding that assumption took three commits: drop the scaling, demote the detected unit to an informational field, then admit nothing ever read that field and delete all 83 lines along with their tests.

What survives is one line:

const STEP_PARAMS = { linearUnit: 'millimeter' }

Passed explicitly rather than left to occt's default. The difference is that "geometry is always millimetres" stops being an inherited coincidence and becomes a written-down contract — so the next person to read this (me, three months later, included) does not have to go read the library's source to find out why nothing downstream converts anything.

That change deleted more lines than it added. That is usually a good sign.

What it still cannot do

  • The cross-section needs a stencil buffer, so it needs WebGL2. Without WebGL the page explains why instead of going blank, but the cross-section is simply absent.
  • Tests cover pure functions only: two files, 19 tests, over dimension formatting and volume / weight maths. There is no automated coverage of DOM or WebGL at all — every capture-pipeline trap above was caught by looking at images side by side.
  • There is no CI. No workflow in the repo, so npm run lint, tsc -b and vitest are things I have to remember to run.
  • The CSP ships as Report-Only, deliberately. The Draco decoder loads from www.gstatic.com and is instantiated through a blob worker, and the occt WASM takes a similar path. In enforcing mode, one wrong directive makes Draco-compressed glTF fail silently — no error, just a spinner that never resolves. Switching it on means first loading a Draco .glb and a .step for real and confirming the console reports no violations.
  • True volume is summed from signed tetrahedra over the mesh triangles, which is only correct on a watertight mesh. The panel carries a note saying so, but the tool does not detect and warn on a particular file — an open mesh (some scanned STLs, for instance) yields a weight that looks entirely plausible and is wrong.

That last one is what I most want to fix. A weight field that is quietly wrong is worse than no weight field.

Try it

https://orthoshot.grayswork.com (Traditional Chinese) · https://orthoshot.grayswork.com/en/ (English)

No sign-up. Drop a part in and you will know within seconds whether it is useful to you.

Contact

Say something specific

Email is fastest. I reply within a working day, usually with more questions than answers.

Or leave a message here
Where
New Taipei, Taiwan · UTC+8
Status
Graduating June 2026 · open to software engineering roles
Gray Tsao© 2026 · Last updated August 2026