Image Capabilities
Fold.page renders images, SVGs, and 3D models as first-class elements in the 3D scene. Standard <img> tags become textured planes with full CSS sizing and opacity support. SVGs can be rasterized as flat textures or converted into extrudable 3D mesh geometry. GLB and GLTF models are auto-scaled to fit any element and composed with the same transform and position pipeline as every other element on the page.
Index
- Basic Image Rendering — Standard
<img>elements rendered as flat textured planes with CSS sizing and opacity.- Back-Face Rendering — Per-element single-sided override via
data-foldpage-backface.
- Back-Face Rendering — Per-element single-sided override via
- Depth Repetition — Stack multiple copies of an image along the Z axis. Without
box-shadow, the whole stack pushes forward toward the camera; withbox-shadow, each layer is styled individually and recedes/directs per layer. - SVG Images — Texture or geometry rendering for
.svgsources and inline<svg>tags.- Texture Mode — SVG rasterized to a flat texture; supports gradients, filters, and masks.
- Geometry Mode — SVG paths converted to 3D mesh geometry with optional extrusion.
- Inline
<svg>Elements — Per-shape depth control for layered 3D compositions in plain markup.
- Lit Materials — Make images and SVGs respond to scene lighting with PBR material presets.
- 3D Models (GLB / GLTF) — Load and auto-scale GLB models at any element's position.
- Auto-scaling — Longest axis scaled to CSS
width;data-foldpage-scaleapplies on top. - Draco Compression — Draco-compressed GLBs handled automatically via a shared decoder.
- Transforms —
data-foldpage-scaleand CSStransformattributes apply. - CSS Position — Static, absolute, fixed, and sticky position routing works the same as all other elements.
- Loading — Models load in the background without blocking the scene.
- Auto-scaling — Longest axis scaled to CSS
Basic Image Rendering
By default, <img> elements inside the .foldpage wrapper are rendered as flat textured planes:
- Size and position follow the image's CSS layout (
width,height,object-fit, positioning). - Transparent PNGs work; nearly transparent pixels (alpha below 0.1) are cut out.
- CSS
opacityis applied. - Both sides are visible by default, following the global
backsidesetting.
No configuration needed.
Back-Face Rendering
By default images are double-sided, so they're visible when rotated to face away from the viewer. To make a specific image single-sided, add data-foldpage-backface="false":
<img src="logo.png"
data-foldpage-depth="7"
data-foldpage-backface="false">This is useful when images form the faces of a 3D composition (e.g. a CSS-positioned cube using rotateY transforms) — suppressing the back face prevents the inside surface from bleeding through when the element rotates. The attribute applies to the image's primary plane and all its depth-repetition copies.
Scope: Currently applies to
<img>elements. The global default is set byFold.init({ backside }). See Data Attribute API → backface for the full attribute reference.
Depth Repetition
data-foldpage-depth creates stacked depth copies of an image along the Z axis. Whether the stack recedes behind the image or the whole stack pushes forward toward the camera depends on whether the image has a CSS box-shadow.
Required attributes:
| Attribute | Description |
|---|---|
data-foldpage-depth | Total depth of the stack in CSS pixels. Determines how many copies are made. |
data-foldpage-depth-interval | Z-spacing between each copy (CSS px). Default: 2. |
Layer count formula: layers = round(depth / interval), clamped to a maximum of 20 layers.
Default (no box-shadow): forward push
With no box-shadow, depth copies use default styling (white tint, base opacity) and the entire stack — including the primary image — is pushed forward toward the camera by the full clamped stack depth (layers × interval). The image bulges out of the page rather than sinking into it.
<!-- depth 9 / interval 2 = round(4.5) = 5 depth copies.
Primary image + all 5 copies shift forward 10px (5 × 2) toward the camera. -->
<img src="card.png"
data-foldpage-depth="9"
data-foldpage-depth-interval="2">With box-shadow: styled, receding layers
Adding a box-shadow opts into per-layer styling and disables the forward push — depth behaves exactly as before, with each shadow layer becoming a separate 3D mesh offset behind (or, with inset, in front of) the primary image.
<!-- depth 9 / interval 2 = 5 depth copies, styled and receding — no forward push -->
<img src="card.png"
data-foldpage-depth="9"
data-foldpage-depth-interval="2"
style="box-shadow:
0 2px 4px rgba(0, 0, 200, 0.5),
0 4px 8px rgba(100, 0, 200, 0.4),
0 6px 12px rgba(200, 0, 255, 0.3),
0 8px 16px rgba(255, 0, 200, 0.2);">Shadow → 3D layer mapping:
| Shadow property | 3D effect |
|---|---|
offset-x / offset-y | Mesh XY offset |
color | Tint applied to the layer mesh |
spread-radius | Mesh scale (positive = larger shadow copy) |
blur-radius | Influences layer opacity |
inset | Reverses direction (inset = layer in front rather than behind) |
Layer assignment: Each box-shadow layer styles one depth copy in order. If there are more depth copies than shadow layers, all remaining copies share the styling of the last shadow. This means you can define a general "background echo" with a single shadow and use a larger data-foldpage-depth to get many copies of it.
<!-- 10 copies, all styled by the single box-shadow -->
<img src="logo.png"
data-foldpage-depth="15"
data-foldpage-depth-interval="1.5"
style="box-shadow: 0 0 8px rgba(100, 0, 255, 0.3);">Text has the same forward-push behavior. A text element with no visible background and no CSS
text-shadowtreatsdata-foldpage-depth/data-foldpage-depth-intervalthe same way images do in the no-box-shadowcase — synthesizing shadow-style layers (50% opacity, inheriting the text color) and pushing the whole stack forward. See CSS Properties → Text Shadows.
SVG Images
<img> elements with .svg sources, and inline <svg> tags within the wrapper, are automatically routed through the SVG pipeline. Two modes are available, and the default depends on how the SVG is placed:
| SVG placement | Default mode |
|---|---|
<img src="….svg"> | Texture |
Inline <svg> | Geometry — or texture when the SVG uses texture-only features (<linearGradient>, <radialGradient>, <filter>, <clipPath>, <mask>, <text>/<tspan>, <image>, <foreignObject>, SMIL or CSS animation) and has no data-foldpage-depth |
Set data-foldpage-svg-mode="texture" or "geo" to choose the mode explicitly.
Texture Mode
The SVG is rendered to an offscreen canvas and used as a flat texture. This handles complex SVGs with gradients, filters, masks, and clip paths.
<img src="complex-illustration.svg">Geometry Mode
SVG paths are parsed and converted into 3D mesh geometry. Each closed path becomes a flat polygon, with optional extrusion depth.
<img src="logo.svg" data-foldpage-svg-mode="geo" data-foldpage-depth="40" data-foldpage-lit="metal">Geometry mode supports:
data-foldpage-depth— extrudes paths forward (positive) or backward (negative) in CSS pixelsdata-foldpage-lit— any material preset (see below)- Per-path colors from SVG
fillattributes - Complex multi-path logos and icons
Inline <svg> Elements
Inline <svg> tags placed directly in the page are supported without any additional configuration. Fold detects them automatically and routes them through the geometry pipeline, giving each path its own mesh. (An inline SVG that uses texture-only features and has no data-foldpage-depth falls back to texture mode — add data-foldpage-svg-mode="geo" to force geometry.)
The most useful feature of inline SVGs is per-element depth: adding data-foldpage-depth to individual child shapes controls how far that shape is extruded forward, independently of the others. Combined with data-foldpage-lit on the root <svg>, this lets you build layered 3D compositions entirely in markup:
<svg data-foldpage-lit xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 320">
<!-- Base background — sits at z=0 -->
<rect width="640" height="320" fill="#111720"/>
<!-- Card surface — raised 30px above the background -->
<rect data-foldpage-depth="30"
x="36" y="36" width="568" height="248" rx="18"
fill="#1f252c" stroke="#313944"/>
<!-- Avatar circle — floats 40px above the background -->
<circle data-foldpage-depth="40"
cx="164" cy="160" r="60"
fill="#6bb7ff"/>
<!-- Heading bar — raised 70px -->
<rect data-foldpage-depth="70"
x="268" y="106" width="220" height="30" rx="8"
fill="#8ad7c1"/>
<!-- Subheading bar — raised 100px, closest to the viewer -->
<rect data-foldpage-depth="100"
x="268" y="156" width="160" height="24" rx="8"
fill="#ffd27a"/>
</svg>Each shape becomes a separate mesh at its own Z position. The data-foldpage-lit on the root <svg> applies a lit material to every child mesh simultaneously — the same as placing it on a parent HTML element. Shapes without a data-foldpage-depth attribute render flat at Z=0.
Lit Materials
By default, images and SVGs are unlit — they show their exact colors regardless of scene lighting. To make an image respond to lights, add data-foldpage-lit:
<img src="coin.png" data-foldpage-lit="metal">
<img src="logo.svg" data-foldpage-svg-mode="geo" data-foldpage-lit="glossy">Available presets:
| Preset | Description |
|---|---|
| (absent) | Unlit — full color, no lighting response |
standard | Balanced PBR — responds to lights, roughness 0.5 |
reflective | Chrome mirror — roughness 0, metalness 0.5 |
rough | Diffuse matte — roughness 1, metalness 0 |
glossy | Shiny plastic — roughness 0.15, metalness 0 |
metal | Brushed metal — roughness 0.2, metalness 0.6 |
glow | Emissive — glows with its own color, independent of lighting |
vivid | Lambertian + 50% self-emission — colors stay close to CSS values while responding to lights |
data-foldpage-lit can also be placed on a parent element to apply to all descendant meshes simultaneously — see Data Attribute API — Lighting.
3D Models (GLB / GLTF)
Any HTML element can host a 3D model by adding data-foldpage-model-src. Fold loads the file, auto-scales the model to fit the element's CSS width, and positions it in the 3D scene exactly like every other Fold element — inheriting all transform attributes and CSS position routing.
Like any Fold attribute, the host element still has to fall within a .foldpage target to be discovered — either as a descendant of one elsewhere on the page, or, most commonly for a single model anchor, by carrying .foldpage directly on itself:
<!-- An empty div becomes a 3D model anchor. .foldpage here makes this div its
own independent scoped target — it doesn't need to be wrapped in a
separate .foldpage container, and it doesn't need any children either;
Fold checks the .foldpage element itself for content, not just its descendants. -->
<div class="foldpage my-model"
data-foldpage-model-src="/models/plant/scene.glb"
style="width: 300px; height: 300px;">
</div>If this div already lives inside a page or section that's already .foldpage-scoped (a wrapping .foldpage ancestor, or Fold.init({ foldpage: true })), you can drop the .foldpage class here — it'll be discovered as a normal descendant instead.
Auto-scaling
The model's longest axis (X, Y, or Z) is uniformly scaled to match the element's CSS width. This ensures the model fills its container without stretching.
If the element has no width, the parent's width is used, then half the viewport width.
After auto-scaling, all data-foldpage-scale / CSS scale() transforms are applied on top, so you can fine-tune size without losing the fit behavior.
Draco Compression
Standard and Draco-compressed GLBs are handled by the same loader instance — no extra configuration needed. Fold shares a single DRACOLoader (decoder fetched from the Google CDN on demand) across all models.
Transforms
Like any other element, a model is positioned and rotated with CSS transform, so it follows media queries and other responsive CSS:
| Attribute | Effect |
|---|---|
data-foldpage-scale | Uniform scale multiplier (applied after auto-fit) |
data-foldpage-scale-origin | Custom pivot point for scale (Y positive = down, CSS convention) |
CSS transform | translate, rotate, scale — all parsed and composed |
CSS transform-origin | Pivot for CSS transform |
<!-- Model floated forward and tilted -->
<div data-foldpage-model-src="/models/trophy.glb"
style="width: 200px; height: 200px;
transform: translateZ(60px) rotateY(15deg) rotateX(-5deg);">
</div>CSS Position
GLB elements follow the same CSS position routing as all other Fold elements:
CSS position | Behavior in 3D |
|---|---|
static / relative | Placed in the scroll scene at DOM position |
absolute | Positioned relative to nearest positioned ancestor |
fixed | Rendered in the fixed overlay group — stays in view |
sticky | Dual scroll + fixed instances, toggled at stick threshold |
Loading
Models are loaded lazily so the scene renders without blocking while the GLB fetches; the model appears once loaded with no layout shift.
Tip: For best results, give the host element explicit
widthandheightin CSS.

