Skip to content

Three.js Teapot

The Utah teapot rendered by Three.js, with an Adwaita sidebar that changes the geometry and the material while it spins.

Three.js runs unmodified: THREE.WebGLRenderer, TeapotGeometry and OrbitControls come straight from npm and draw into a Gtk.GLArea. The controls beside it are real GTK widgets bound to the scene, so you can wrap a web renderer in a native GNOME interface instead of rebuilding one in HTML.

Ported from the upstream webgl_geometry_teapot example.

It declares all four runtimes, so pick the one you have:

Terminal
gjsify showcase three-geometry-teapot --runtime gjs
gjsify showcase three-geometry-teapot --runtime node
gjsify showcase three-geometry-teapot --runtime bun
gjsify showcase three-geometry-teapot --runtime deno

A GTK 4 window opens as an Adw.OverlaySplitView. The sidebar holds the controls, the canvas fills the rest. Drag to orbit the camera, scroll to zoom. Same window and same scene on all four.

The sidebar has two groups. Geometry sets the tessellation level (an Adw.ComboRow stepping 2, 3, 4, 5, 6, 8, 10, 15, 20, 30, 40, 50) and toggles Display Lid, Display Body, Display Bottom, Snug Lid and Original Scale. Material switches the shading between wireframe, flat, smooth, glossy, textured and reflective.

--runtime gjs runs the --app gjs bundle against GJS’s native gi://. The other three share one --app node bundle, where gi:// goes through the node-gi bridge. Leave the flag off and Choose the runtime says what the CLI picks.

TypeScript
import { mount } from '@gjsify/example-dom-three-geometry-teapot/browser';
const handle = mount(document.getElementById('app')!, {
assetBase: '/demos/teapot/', // where the UV grid and the Pisa cubemap live
});
handle.pause();
handle.resume();

The browser build rebuilds the same sidebar out of @gjsify/adwaita-web custom elements, so both versions look and behave the same.

PackageWhat Three.js needs it for
@gjsify/webglThe whole WebGL 2.0 API: vertex array objects, uniform buffer objects, texStorage2D, drawElementsInstanced
@gjsify/dom-elementsHTMLCanvasElement and requestAnimationFrame
@gjsify/event-bridgeMouse drag and scroll feeding OrbitControls
@gjsify/adwaita-webThe sidebar in the browser build
LibadwaitaAdw.OverlaySplitView, Adw.ComboRow and Adw.SwitchRow in the GTK build

@gjsify/webgl maps WebGL calls onto the platform’s own GL through a Vala bridge, which is GLES 3.2 on Linux. Three.js targets desktop WebGL, does not know about that difference, and does not have to. Bridge widgets has the detail.

showcases/dom/three-geometry-teapot/

  • src/three-demo.ts is the whole scene behind one start(canvas) function, with no platform imports. It also exports TESS_VALUES and SHADING_VALUES so both shells build the same control lists.
  • src/gjs/gjs.ts, teapot-window.ts and teapot-window.blp are the Adw.Application, the window class and its Blueprint UI.
  • src/browser/browser.ts exports mount(); browser-main.ts runs it full-page.
  • src/assets/ carries the UV grid texture and the Pisa cubemap, published through the package’s ./assets/* export.

Exporting your control values from the shared module, as TESS_VALUES does here, is the cheap way to stop two shells drifting apart.