Screenframe3D
Composite an app screenshot onto a real 3D phone. Live demo.
Phone GLBs are not included in the npm package (Sketchfab licenses). You download a model once, drop it in public/models/, and Screenframe does the rest. First-party devices are planned.
Install
npm install screenframe3d three
React apps also need react / react-dom. Vite apps should add the plugin below so the HDRI (and later, posters) land automatically.
1. Get a device model from Sketchfab
device is a path. Screenframe loads /models/{device}.glb from your site.
| Device | device id |
Put the file at | Sketchfab |
|---|---|---|---|
| iPhone 5s | apple/iphone-5s |
public/models/apple/iphone-5s.glb |
IPhone 5s by Eternal Realm (CC-BY-4.0) |
| iPhone 12 Pro | apple/iphone-12-pro |
public/models/apple/iphone-12-pro.glb |
iPhone 12 Pro by DatSketch (CC-BY-4.0) |
| iPhone 14 Pro | apple/iphone-14-pro |
public/models/apple/iphone-14-pro.glb |
No single listing is embedded in the file. Search downloadable iPhone 14 Pro models. You want a body_mat (or similar) whose emissive map includes a bright portrait screen. |
| iPhone 17 Pro | apple/iphone-17-pro |
public/models/apple/iphone-17-pro.glb |
Iphone 17 pro by Ibrahim.Bhl (CC-BY-4.0) |
Download steps
- Make a free Sketchfab account.
- Open the model. Confirm it is downloadable and that the license fits your project (CC-BY requires credit).
- Download 3D model → glTF (or GLB if offered).
- If you get a folder, use the
.glb(oftenscene.glb). If you only getscene.gltf+scene.bin+textures/, keep that folder structure or convert to a single.glb. - Rename / move it to the path in the table. The filename must match
deviceexactly:apple/iphone-12-pro→models/apple/iphone-12-pro.glb.
What Screenframe looks for on the glass
Any downloadable phone can work if Screenframe can find the display:
- A material or mesh named
screen,wallpaper,Screen_BG,display, orlcd, or - A material named
body_matwhose emissive map has a bright portrait rectangle (atlas).
The four rows above are the combinations this repo is tested against. Catalog in code: DEVICES from screenframe3d.
2. Environment map (glass and metal)
Reflections use /env/greenwich_park_1k.hdr by default (Greenwich Park, CC0, Poly Haven).
The Vite plugin copies that file into public/env/ when it is missing. Without the plugin, download the 1K HDR and save it as public/env/greenwich_park_1k.hdr.
3. Vite plugin (recommended)
import { defineConfig } from 'vite';
import { screenframe } from 'screenframe3d/vite';
export default defineConfig({
plugins: [screenframe()],
});
That plugin:
- Copies the bundled HDRI into
public/env/ - Writes extracted device textures to
public/textures/on first run - Captures a still (poster) so the next load can show an image before WebGL is ready
It does not download Sketchfab models.
4. Use it
Vanilla
<div id="device"></div>
<script type="module">
import { initDeviceShowcase } from 'screenframe3d';
initDeviceShowcase({
container: document.getElementById('device'),
screenshot: '/screenshot.png',
device: 'apple/iphone-12-pro',
publicBase: import.meta.env.BASE_URL,
});
</script>
React
import { DeviceShowcase } from 'screenframe3d/react';
<DeviceShowcase
screenshot="/screenshot.png"
device="apple/iphone-12-pro"
publicBase={import.meta.env.BASE_URL}
/>
publicBase matters if the app is not served from / (GitHub Pages, a Vite base, etc.).
Options
Passed to initDeviceShowcase() or <DeviceShowcase />:
| Option | Description |
|---|---|
container |
DOM node for the canvas (vanilla only) |
screenshot |
URL of the image on the glass (not downscaled) |
device |
Path id, e.g. apple/iphone-12-pro → /models/apple/iphone-12-pro.glb |
publicBase |
Prefix for models, env, posters, textures |
environment |
HDRI URL (default /env/greenwich_park_1k.hdr) |
fov, zoom, baseTilt |
Camera / rest pose |
tiltEnabled, spring, scrollTilt |
Pointer and scroll motion |
poster |
false / omit = no still. true / 'auto' = hashed /posters/...webp. Or pass a URL. |
cachePoster, cacheDeviceTextures |
Persist stills and maps during vite dev (needs the plugin) |
fallbackCondition, fallbackImage |
Opt-in static image instead of WebGL |
Development (this repo)
npm install
npm run dev # landing + playground (models already in public/)
npm run build # library → dist/
Phone GLBs in public/models/ are for the demo only. They are not published to npm.
License
MIT for the library code.
Device models you download remain under their Sketchfab licenses; CC-BY models need attribution. The default HDRI is CC0 (Poly Haven).