@kuraykaraaslan/kui-viewer
v0.1.0
Published
Browser-based IFC/BIM 3D viewer built on Three.js, web-ifc and @thatopen/fragments. Ships a vanilla TypeScript API plus an optional React subpath.
Downloads
228
Maintainers
Readme
kui-viewer
A browser-based IFC / BIM 3D viewer built on Three.js, web-ifc and @thatopen/fragments. Ships a framework-agnostic TypeScript core plus an optional React subpath.
🔗 Live demo: kui-viewer.kuray.dev

Status:
0.1.0— first minor release. The public API is stabilizing but may still change before1.0.0.
What's new in 0.1.0
- World-class rendering — ACES-Filmic tone mapping, a neutral IBL environment (RoomEnvironment → PMREM), GTAO ambient occlusion + SMAA post-processing, and fat-line selection outlines. Opt out via
postprocessing: false/environment: false. - English-default UI — every label, tooltip and aria-label now ships in English (full i18n layer next).
- Working property panel — Psets/Qtos are extracted in-worker while the model is open and served after it closes.
- IoT sensor sprites resolve — the loader wires the GlobalId → element-centroid map.
- BCF — viewpoint apply restores the full multi-component selection; topics carry
AssignedTo/DueDate/Labels. - Screenshot export —
viewer.screenshot({ filename })(PNG); camera flights respectprefers-reduced-motion. - Configurable measurement units — metric / imperial / feet-inches via
setMeasurementUnits. - OSI-clean dependencies — GSAP replaced with a small dependency-free tween helper.
- Roadmap — a phased plan to world-class lives under
phases/.
Features
- IFC parsing in a Web Worker via
web-ifc(single-threaded WASM), with in-worker Pset/Qto extraction - Three.js scene with orbit controls, render loop, RTC origin handling for large coordinates
- Post-processing pipeline — ACES tone mapping, IBL environment, GTAO ambient occlusion, SMAA, fat-line selection (all opt-out)
- BVH-accelerated picking and selection (
three-mesh-bvh) - Multi-model federation, spatial tree, discipline detection, per-model visibility
- Property panel + spatial tree UI primitives (vanilla TS + Tailwind v4 tokens, English default)
- Measurement (distance / angle / area) with configurable units, clipping planes, BCF 2.1 import/export, IoT sensor sprites
viewer.screenshot()PNG export;prefers-reduced-motion-aware camera animation- Optional React subpath:
<KUIViewer />,ViewerProvider, hooks, headless components - Strict TypeScript, Zustand vanilla stores, Zod-validated DTOs at boundaries
Install
pnpm add @kuraykaraaslan/kui-viewer three zustand zod
# optional, for the React subpath:
pnpm add react react-domreact and react-dom are listed as optional peerDependencies — only required if you import from @kuraykaraaslan/kui-viewer/react.
WASM setup (required)
The IFC parser worker loads web-ifc.wasm from /wasm/ at runtime. Copy the WASM file from web-ifc's package to your app's static folder before serving:
mkdir -p public/wasm
cp node_modules/web-ifc/web-ifc.wasm public/wasm/Or wire it into your build's prebuild/predev lifecycle. The repo's scripts/copy-wasm.mjs is a working example.
Quick start — vanilla TypeScript
import { Viewer, IfcLoaderService, FragmentsLoaderService } from "@kuraykaraaslan/kui-viewer";
import "@kuraykaraaslan/kui-viewer/styles.css";
const container = document.getElementById("viewer")!;
const viewer = new Viewer({ container });
const parsed = await IfcLoaderService.load({
kind: "url",
url: "/models/duplex.ifc",
});
const model = await FragmentsLoaderService.attach(viewer, parsed, "/models/duplex.ifc");
// later
viewer.dispose();Quick start — React
import { KUIViewer, ViewerProvider, useLoadIfc, useSelection } from "@kuraykaraaslan/kui-viewer/react";
import "@kuraykaraaslan/kui-viewer/styles.css";
function App() {
return (
<div className="h-screen w-screen">
<KUIViewer>
<ViewerProvider>
<Loader url="/models/duplex.ifc" />
<SelectionReadout />
</ViewerProvider>
</KUIViewer>
</div>
);
}
function Loader({ url }: { url: string }) {
const { load, error } = useLoadIfc();
React.useEffect(() => {
load(url).catch(() => {});
}, [load, url]);
return error ? <pre>{error.message}</pre> : null;
}
function SelectionReadout() {
const { selectedElementId, clear } = useSelection();
if (!selectedElementId) return null;
return (
<button onClick={clear}>Selected: {selectedElementId} (clear)</button>
);
}Exports
| Specifier | Contents |
|---|---|
| @kuraykaraaslan/kui-viewer | Vanilla core: Viewer, IFC/fragments loaders, federation/spatial-tree stores, UI primitives, BIM domain services |
| @kuraykaraaslan/kui-viewer/react | React wrapper + hooks (useLoadIfc, useFederation, useSelection, useSpatialTree, …) and headless components |
| @kuraykaraaslan/kui-viewer/styles.css | Compiled Tailwind v4 tokens. Import once at the app root |
Stack
- Three.js (r170)
- web-ifc (IFC parsing, WASM)
- @thatopen/fragments (fragment scene graph)
- three-mesh-bvh (picking acceleration)
- Zustand v5 (vanilla stores)
- Zod v4 (DTO validation)
- Tailwind CSS v4 (design tokens)
Development
pnpm install
pnpm dev # Vite playground at http://localhost:5173
pnpm build # JS + .d.ts + styles.css → dist/
pnpm test # Vitest core (node) + react (jsdom)
pnpm lint
pnpm typecheckProject layout
modules/— vanilla core (scene, renderer, viewer-core, IFC pipeline, BIM domain). React/JSX prohibited.libs/— cross-cutting utilities (env, logger,AppError,cn(), Three helpers, workers).react/— optional React subpath (<KUIViewer />,ViewerProvider, hooks, headless components).src/— Vite dev playground (not bundled into the published package).phases/— world-class roadmap (Phase 0–6).docs/events.md— auto-generated viewer event catalog (pnpm events:catalog).
License
Apache-2.0 © 2026 Kuray Karaaslan
