A browser-based OpenSCAD editor with live 3D preview, refactored from the
React/Webpack original to a Lit + biu frontend with an optional flat-file
TypeScript backend (Bun / Deno) for openscad CLI access.
Inspired by and migrated from https://github.com/openscad/openscad-playground.
├── src/ # Frontend (Lit Web Components, no JSX/TSX)
│ ├── components/ # <openscad-app> and panels
│ │ ├── editor-panel.ts
│ │ ├── viewer-panel.ts
│ │ ├── customizer-panel.ts
│ │ ├── file-picker.ts
│ │ ├── export-button.ts
│ │ └── multimaterial-colors-dialog.ts
│ ├── state/ # Reactive store (model.ts / store.ts / types.ts ...)
│ ├── runner/ # OpenSCAD WASM worker glue
│ ├── fs/ # BrowserFS + zip-archive mounting
│ ├── language/ # Monaco language config & completions
│ ├── io/ # Import / export helpers (glTF, 3MF, ...)
│ ├── _share/ # Shared utilities reused by frontend & backend
│ ├── app.ts # Top-level layout
│ ├── footer.ts # Status bar + actions
│ ├── index.ts # Entry point, mounts <openscad-app>
│ ├── index.html
│ └── index.css
├── static/ # Static assets served as-is
│ ├── wasm/ # openscad.wasm + openscad.js (built locally)
│ ├── libraries/ # Bundled OpenSCAD library .zip archives
│ ├── examples/ # Sample .scad files (file-picker entries)
│ ├── fonts/
│ ├── browserfs.min.js
│ ├── model-viewer.* / skybox-lights.jpg / axes.glb / complete.wav
│ ├── manifest.json + favicon.ico + logo*.png
│ └── gh-fork-ribbon.min.css
└── dist/ # Build output (generated by `biu`)
The frontend is a single-page Lit app; the (optional) backend dynamically imports route files based on HTTP method and URL path, with no build step or route registration needed.
- Framework: Lit 3 (Web Components, no JSX/TSX)
- Bundler / dev server:
biu— zero-config, Bun-powered - Editor: Monaco Editor (via
@monaco-editor/loader) - 3D Viewer:
<model-viewer> - Virtual FS: BrowserFS with
ZipFS-mounted library archives so users can browse the bundledexamples/andlibraries/trees from the file picker.
- On startup the app calls
model.init()to automatically render the defaultplayground.scad(or whatever source was restored from URL hash / localStorage) — no need to press F5. - Switching to another
.scadexample via the file picker resets the Monaco editor's cursor and viewport to the very beginning of the new file. - Library
.ziparchives are fetched from the site root (/libraries/) regardless of the worker's URL, so moving the worker bundle (e.g. into/runner/…) does not break archive loading.
- Bun (required by
biu) biuitself: https://github.com/mindon/biu
static/wasm/ and static/libraries/ are not committed — they are produced by
the original webpack-based build pipeline (kept under the project root). To
populate them:
-
Clone the upstream repo (or use this monorepo's root).
-
From the root of the upstream/clone, run:
bun run build:libs:wasm # builds public/openscad.{js,wasm,...} bun run build:libs # builds public/libraries/*.zip
-
Copy the artifacts into this project:
cp -R ../public/openscad.* static/wasm/ cp -R ../public/libraries/*.zip static/libraries/
(static/examples/ and static/fonts/ ship with the repo.)
# Dev server (biu, hot reload)
bun run dev # alias for: biu --serve
# Production build → dist/
bun run build # alias for: biu
# Preview the production build
bun run preview # biu --serve 3000A pure-frontend deployment is fully functional thanks to the WASM build of OpenSCAD.
| File | Description |
|---|---|
src/index.ts |
Entry point; mounts <openscad-app>, kicks off model.init() |
src/index.html / src/index.css |
HTML shell & global styles |
src/app.ts |
Top-level layout (<openscad-app>) |
src/footer.ts |
Status bar + action buttons |
src/state/store.ts / model.ts / types.ts |
Reactive state management |
src/state/default-scad.ts |
Default playground.scad source |
src/components/editor-panel.ts |
Monaco editor wrapper (resets to top on file switch) |
src/components/viewer-panel.ts |
3D / 2D model viewer |
src/components/customizer-panel.ts |
OpenSCAD parameter customizer |
src/components/file-picker.ts |
Examples / libraries / user files browser |
src/components/export-button.ts |
Export menu (STL / 3MF / GLB / …) |
src/components/multimaterial-colors-dialog.ts |
Color picker for multi-material 3MF export |
src/runner/openscad-worker.ts |
Web Worker glue around OpenSCAD WASM |
src/fs/zip-archives.ts / filesystem.ts |
BrowserFS + zip mounts |
src/language/openscad-completions.ts |
Monaco autocomplete for OpenSCAD |
- React + PrimeReact + Webpack → Lit + biu.
- React Context → small reactive
appStoreinsrc/state/store.ts. - Same WASM core (
openscad.wasm) and same.zip-packaged libraries. - Build output ends up in
dist/; the upstreamdist/at the repo root is left untouched.