Devtools
Add a panel to your app while you develop. It checks your live scene, shows what a render will cost, and renders images, videos and bakes with a click.
Mount it
Mount the panel next to your canvas. In React Three Fiber, give it your key or
token the way you give useBakery3(), and it shows the scene in the page's one
<Canvas>.
import { Bakery3Devtools } from '@oeave/bakery3/r3f';
<>
<Canvas>
<Scene />
</Canvas>
{import.meta.env.DEV && <Bakery3Devtools token={getToken} />}
</>With several canvases on the page, put a <Bakery3Provider> inside the one to
render and mount <Bakery3Devtools /> without a key or token. It then uses the
provider's connection and scene.
<>
<Canvas>
<Bakery3Provider token={getToken}>
<Scene />
</Bakery3Provider>
</Canvas>
{import.meta.env.DEV && <Bakery3Devtools />}
</>With neither a key nor a provider, the panel still opens after a moment. It says that no key or token was given, links to the Keys page, and logs one warning in the console.
Options
Both mounts take these:
| Option | What it does |
|---|---|
position | 'bottom-right' (default), 'bottom-left', 'top-right' or 'top-left'. |
defaultQuality | The quality preset the Render and Video tabs open on. Default 'studio'. |
defaultOpen | Start open instead of as a small button. |
defaultTab | 'render', 'video' or 'bake'. |
timeline | The timeline the Video tab films. Without one the panel makes its own on your live camera. |
timelineTargets | Objects offered in the timeline's "add track" picker. |
onPlaybackChange | Called when the timeline takes the camera and when it gives it back. |
bakeTransport | Bake with your own worker instead of the hosted one. |
bakeBaseUrl | Where relative URLs in a bundle from your own worker resolve. |
muteLightsOnApply | Turn your live lights off when a bake is applied. Off by default, because the lightmap already replaces their diffuse light. See Bake a scene. |
history | { list, download? }: your own list of past renders, shown as Recent instead of the panel's own list. |
<Bakery3Devtools> also takes these. Leave out apiKey and token to use
<Bakery3Provider> instead.
| Option | What it does |
|---|---|
apiKey | A secret key, for testing on your own machine. |
token | A function that fetches a browser token, as useBakery3() takes it. |
baseUrl, fetch | The API address and the fetch to use, with apiKey or token. |
defaults | Defaults for every render, as createBakery3() takes them, with apiKey or token. |
onReady | Called with the panel's handle when it mounts, and with null when it unmounts. See below. |
mountBakery3Devtools also takes these:
| Option | What it does |
|---|---|
bakery3 | The client. Required. |
scene, camera | The live scene and camera. Required. Getters work, so the panel can follow a camera you swap. |
renderer | Your WebGLRenderer, so the render uses your color space and tone mapping. A getter works here too. |
container | Where the panel mounts. Defaults to document.body. |
title | A name beside the logo in the header. None by default. |
onOpenChange | Called when the panel is opened or collapsed. |
What the panel shows
| Part | What you get |
|---|---|
| Compatibility | Beside each button, how much of your scene renders as you see it, such as 98% compatible. Click it for each object that renders differently, with a link to the fix. The check is free and runs in your browser. |
| Price | The price beside each button, from estimate(). Hover it to see what it is made of (for a video, the frame count times the price of one frame) and the most it can cost. It updates when you change a setting, the scene or the camera, once the change has settled, and is not fetched again while nothing changes. |
| Render | Quality and size. The size is the long edge, and the picture takes your camera's shape. |
| Video | The timeline: scrub, add keyframes, record the camera with Keyframe camera, and set the length and fps, which give the frame count. The price is the clip's, quoted the way the clip is charged. |
| Bake | The objects that can be baked (and why others cannot), and your bakes under Recent. Click a bake to put it on the scene, and click it again to go back to the live scene. Hold to compare shows the scene without the bake. |
| Jobs | A row for every render, from the moment you press the button: capturing the scene, uploading, then the render's own progress and preview, with a button that cancels it. Press Render again to start another while one runs. In React Three Fiber, renders your own useBakery3() button starts show here too. |
| Recent | Every render, video and bake you made, newest first, with its size, quality and price. Click one to see it again. The list is kept in this browser, so it is still there after a reload, until its links expire after 24 hours. |
A missing or wrong key, a token the API refused and an empty balance show in the panel as soon as it asks for a price, with a link to the Keys page or the Billing page.
Control the panel from code
mountBakery3Devtools() returns the panel's handle. <Bakery3Devtools> hands
you the same handle through onReady:
import type { DevPanel } from '@oeave/bakery3/r3f';
const panel = useRef<DevPanel | null>(null);
<Bakery3Devtools onReady={(handle) => (panel.current = handle)} />The handle does this:
panel.open();
panel.close();
panel.setTab('bake');
panel.refresh(); // re-read bake candidates and history
panel.refreshCost(); // ask for a fresh estimate now
panel.setStatus('uploading 148 MB…', 'info'); // 'info' | 'error' | 'ok'
panel.setProgress('Baking pedestal · 3/12', 0.25);
panel.setProgress(null); // clear
panel.dispose();dispose() also reverts any bake the panel applied and frees its lightmaps. In
React that happens on unmount.
Pause your orbit controls during playback
Orbit controls and the timeline both move the camera, and the preview stutters if both run. Turn your controls off while the timeline plays.
<Bakery3Devtools
token={getToken}
timeline={tl}
onPlaybackChange={(active) => {
controls.current.enabled = !active;
}}
/>Bake with your own worker
The Bake tab calls bakery3.bake() by default, which is our hosted baking. To
use a bake worker of your own, pass a bakeTransport: a function that takes a
BakeSpec and its assets and returns a BakeBundle.
<Bakery3Devtools
token={getToken}
bakeTransport={async (spec, assets) => {
const response = await fetch('/api/bake', {
method: 'POST',
body: pack(spec, assets), // your own function
});
return response.json();
}}
/>If your bundle has relative URLs, set bakeBaseUrl to where they resolve.
Keep the panel out of production
{process.env.NODE_ENV === 'development' && <Bakery3Devtools token={getToken} />}@oeave/bakery3/devtools is its own entry point, so an app that never imports
it never bundles it. You can ship the panel in production behind your own access
control, but it is made for development.
Use the panel without the hosted API
mountDevPanel is the same panel with no client attached. You supply every
callback. Use it when the panel talks to a local worker or a proxy of your own.
import { mountDevPanel } from '@oeave/bakery3/devtools';
const panel = mountDevPanel({
position: 'bottom-right',
compatibility: () => report,
estimate: (request) => post('/api/estimate', request),
sceneVersion: () => scene.children.length,
viewVersion: () => camera.position.toArray().join(),
render: { onRender: (settings) => post('/api/render', settings) },
});sceneVersion and viewVersion are cheap fingerprints of what is in the scene
and where the camera is. The panel compares them a few times a second while it
is open and asks for a new price when one changes: a change to the scene as
soon as it settles, a camera move once the camera stops.
Show only the timeline
mountTimelinePanel gives you the timeline without the render and bake tabs.
See Render video.
