
Z-axis spatial navigation
This demo arranges views along the Z axis — scrolling flies the camera
forward through depth layers like a corridor. Each panel is positioned using
translateZ(negative) from the origin, with no Y-rotation.
The camera travels straight ahead, deeper into the scene.
data-foldpage-animate
Any element inside .foldpage can carry continuous 3D animation
using data-foldpage-animate. Supply one or more effect names as a
comma-separated value and Fold applies them as a continuous per-frame animation
loop directly on the Three.js object — no CSS keyframes, no JavaScript timers.
Available effects: rotate, sway,
float, pulse, tilt. Effects
compose — combine float,sway for gentle hovering drift, or
tilt with float for an image that follows the cursor
(or gyro on mobile) while slowly bobbing.
<img data-foldpage-animate="tilt,float" data-foldpage-animate-speed="0.8" data-foldpage-animate-amplitude="0.6" src="photo.jpg">
data-foldpage-animate-speed — multiplier for animation rate. Default 1.0; lower is slower, higher is faster.
data-foldpage-animate-amplitude — multiplier for the magnitude of movement or rotation. Default 1.0.
data-foldpage-animate-axis — constrain rotation effects to a specific axis: x, y, or z.
data-foldpage-animate-phase — offset in radians for the animation cycle. Staggers identical effects on sibling elements.
tilt,float,pulse for a card that breathes and tracks the pointer simultaneously.
Images & GLB
<img> elements inside .foldpage render as
textured planes in the Three.js scene. They maintain their CSS
dimensions and layout position exactly — width, height, margins, and any
translateZ offset in your CSS all carry through to the 3D object.
No extra configuration is needed; Fold detects image elements automatically.
For volumetric assets, data-foldpage-model-src loads a
.glb model and positions it in the scene at the element's
bounding-box center. Use data-foldpage-scale and
data-foldpage-rotate-y to adjust presentation without touching
your layout.
<!-- Image rendered as a 3D textured plane --> <img src="photo.jpg" width="400" height="300" data-foldpage-view-link> <!-- GLB model positioned in the scene --> <div data-foldpage-model-src="/models/object.glb" data-foldpage-scale="2" data-foldpage-rotate-y="45" style="width:300px; height:300px;"> </div>
Adding data-foldpage-view-link to an image turns it into a
spatial navigation target. When clicked in 3D, the camera flies
to frame that exact image — centering on the intersection point of the click ray
and the image plane. Large gallery images let the viewer zoom into individual
details by clicking where they want to look.
data-foldpage-view-link to fly the camera directly to that image in 3D space. Click the same image again to dismiss and return to scroll navigation.
Fold.init
Fold.init({}) bootstraps the entire Fold.page runtime. Call it once
after the script tag with an options object. All properties are optional — the
defaults are chosen so a minimal Fold.init({ foldpage: true }) produces
a working scene for most pages.
Fold.init({ // Activate the DOM → 3D pipeline for .foldpage elements foldpage: true, // Show / hide the pulsing loading indicator loadingIndicator: false, // HDRI environment lighting hdrConfig: { path: '/hdr/', file: 'studio_small.hdr', }, // CSS class / data-attribute prefix (default: 'foldpage') serviceSlug: 'foldpage', });
Setting foldpage: true tells Fold to scan the DOM for elements with
the .foldpage class and process their subtree. Each qualifying element
receives the foldpage-el class, which activates your
.myclass.foldpage-el CSS transform rules, and is then hidden in the DOM
while a Three.js equivalent is created in its place.
Enabled by default. W/S move the camera forward and back,
A/D strafe left and right, and R/F rise and
descend. Mouse drag orbits the camera. To disable:
Fold.init({ cameraControls: false }).
A subtle pulsing overlay shown while fonts, textures, and GLB assets are loading.
Set to false to suppress it — useful in demos where assets are local
and load instantly, or when you want a custom loading experience.
Fold can load an HDRI environment map to provide image-based lighting for 3D
objects and GLB models in the scene. Supply a path directory and
file name. The file is loaded asynchronously and does not block the
initial render. Supports .hdr and .exr formats.
The serviceSlug controls the prefix used for all CSS classes and
data-* attributes. The default is 'foldpage', producing
.foldpage, data-foldpage-view,
data-foldpage-animate, etc. Change this if you need to embed Fold into
a page that already uses these class names for other purposes.
After Fold.init(), the Fold global exposes a small
runtime API for querying and updating the live scene:
// Update options after init (re-triggers affected systems) Fold.set({ cameraControls: false }); // Access the underlying Three.js Scene object const scene = Fold.getScene(); // Access the PerspectiveCamera const camera = Fold.getCamera(); // Run a callback once the scene is ready (assets loaded) Fold.onReady((scene, camera) => { // safe to query objects here });
Many Fold.init options have equivalent data-foldpage-*
attribute counterparts that can be set directly on HTML elements. This lets you
configure per-element behaviour without JavaScript — useful for CMS-driven content
where you control the markup but not the script.
<!-- Per-element options via data attributes --> <section data-foldpage-view data-foldpage-view-rotation="0 15 0" data-foldpage-view-origin="0 40 0" > Off-axis view with lowered focal point </section> <img src="photo.jpg" data-foldpage-animate="float,tilt" data-foldpage-animate-speed="0.6" data-foldpage-animate-amplitude="0.5" >
Fold maps CSS pixels to Three.js world units using a fixed scale factor of
0.0007 units/px (pixelSizeDefault). At this scale,
1000 CSS pixels in Z equals 0.7 world units — roughly one camera-distance.
Axis directions match CSS 3D convention: +X right,
+Y down, +Z toward the viewer.
Negative translateZ pushes elements away from the camera;
positive values bring them forward.
The default camera starts at approximately z = +0.7 world units
looking toward the origin. As you scroll through views, Fold moves the camera
along the path defined by the view elements' surface normals, using spring
physics so each transition feels weighted and natural rather than mechanical.
translateZ(-1800px) translateX(-110px). It is taller than the viewport so the camera pans through it before advancing to depth-4. Panels that exceed the viewport height give the viewer time to read before the next waypoint triggers.
By default the camera positions itself along a view element's surface normal — directly in front of the panel, at a distance that fits the element comfortably in frame. Two override attributes give finer control:
data-foldpage-view-rotation="x y z" — sets explicit Euler angles
(degrees) for the camera when this view is active. Use it to approach a panel
from a diagonal or to maintain a dramatic angle.
data-foldpage-view-origin="x y z" — shifts the focal point of the
view in CSS pixel units relative to the element's center. Positive Y moves the
focal point downward; negative Y moves it upward. Useful for tall panels where
you want the camera to frame a specific region rather than the geometric center.
About
Fold.page renders ordinary HTML content into a live WebGL scene — no separate 3D authoring tool, no model exports, no scene graph editor. The same HTML that displays in a standard browser also navigates in a spatial environment, and the same markup works in WebXR for immersive VR and AR viewing.
The pipeline reads the existing DOM, extracts computed styles and layout positions,
hides the original elements, and reconstructs them as Three.js meshes. Text becomes
Troika text geometry; images become textured planes; backgrounds become
RoundedPlane meshes. CSS transforms are read and applied directly to
the Three.js objects — which is how this Z-axis corridor works.
Because layout and styling are pure CSS, the same page degrades gracefully to a
standard scrolling document when Fold is not loaded. No JavaScript framework lock-in,
no custom build pipeline — just a script tag and the .foldpage class.
translateZ(-2600px). The camera has traveled approximately 1820 CSS pixels into Z space to get here from the home origin.