Quickstart

Install the SDK, connect your Three.js scene and get a realistic render of it back. Every example comes in a Three.js and a React Three Fiber version.

Install

npm install @oeave/bakery3

The SDK uses the copy of Three.js your app already has, r160 or newer. A few entries need a newer one: @oeave/bakery3/presets and /catalog r161, /tsl r170, and /environments and /shapes r171. React and @react-three/fiber are only needed if you import @oeave/bakery3/r3f.

Your first render

Create a secret key on the Keys page, and sign up there first if you have no account. Connect your scene and camera, then call render(). To try it on your own machine, you can put your secret key straight into the browser code.

import { useBakery3 } from '@oeave/bakery3/r3f';

function RenderButton() {
  const { render, result } = useBakery3({ apiKey: 'bk_sk_…' });

  return (
    <>
      <button onClick={() => render({ quality: 'preview' })}>Render</button>
      {result && <img src={result.url} alt="Render" />}
    </>
  );
}

Put the button next to your <Canvas>, not inside it. It renders the scene in your canvas. With more than one canvas on the page, see below.

Let your users render without exposing your API key

Your users start renders from the browser, but your secret key has to stay on your server. Add one small endpoint that uses the key to make a short-lived token. The browser asks it for a token whenever it needs one.

app/api/bakery3-token/route.ts
import { Bakery3Server } from '@oeave/bakery3/node';

const bakery3 = new Bakery3Server(process.env.BAKERY3_SECRET_KEY!);

export async function POST() {
  return Response.json(
    await bakery3.tokens.create({
      expiresInSeconds: 300,
      maxRenders: 1,
      maxCost: 1,
    })
  );
}

A token is safe to hand to a browser because of its limits. It expires, it can start at most maxRenders renders and it can spend at most maxCost dollars. It can still follow the renders it started after that, so maxRenders: 1 is enough for one click. Auth & keys has every option.

Before you deploy, add allowedOrigins: ['https://shop.example.com'] with your own site, so a token copied out of the network tab works nowhere else. Leave it out while you test on localhost, or the token is refused there. See Restrict a token to your site.

Connect the SDK

Point the SDK at your token endpoint instead of a key.

import { useBakery3 } from '@oeave/bakery3/r3f';

const getToken = () => fetch('/api/bakery3-token', { method: 'POST' }).then((r) => r.json());

function RenderButton() {
  const { render } = useBakery3({ token: getToken });
  // …
}

Render

import { useBakery3 } from '@oeave/bakery3/r3f';

function RenderButton() {
  const { render, isRendering, result } = useBakery3({ token: getToken });

  return (
    <>
      <button disabled={isRendering} onClick={() => render({ quality: 'studio' })}>
        Create studio render
      </button>
      {result && <img src={result.url} alt="Studio render" />}
    </>
  );
}

The SDK reads your objects, materials, camera and lights at the moment you call render(), so the image shows the scene as it looks right then. result in the hook is the latest render it started that has finished. Render image covers size, file format and transparent backgrounds. Render video does the same for a clip.

Show a preview while it renders

job.result() waits for the finished image. To show something sooner, listen to the job's events.

const job = await render();

job.on('preview', ({ url }) => setImage(url));       // a quick, grainy version first
job.on('progress', ({ percent }) => setProgress(percent));

const final = await job.result();                     // the finished image

See the price before you render

const { estimate, render } = useBakery3({ token: getToken });

const cost = await estimate({ quality: 'studio', width: 2048, height: 2048 });
// { estimatedCost: 0.12, maximumCost: 0.16, estimatedSeconds: 7, compatibilityScore: 0.97 }

await render({ maxCost: 0.14 });
// throws RenderBudgetExceeded before anything is rendered, nothing charged

You are never charged more than the maximum you accepted when the render started. See Cost & estimates.

Check your scene without an account

check() runs in the browser, needs no account and sends nothing anywhere. It tells you what will render as it is and what to fix first.

const report = bakery3.check();

report.score;   // 0.97
report.issues;  // [{ severity: 'warning', message: 'MeshLambertMaterial "Lamp" is approximated. …', path: '<Lamp>', … }]

Materials lists what is supported.

Render from several canvases or components

useBakery3({ token }) finds your scene on its own when the page has one <Canvas>. Use <Bakery3Provider> instead when the page has several canvases, or when several components render and should share one progress bar. Put the provider inside the canvas you want to render, and call useBakery3() and <Bakery3Devtools /> without options anywhere on the page.

import { Bakery3Provider, useBakery3 } from '@oeave/bakery3/r3f';

<Canvas>
  <Bakery3Provider token={getToken}>
    <Sofa />
  </Bakery3Provider>
</Canvas>

function RenderButton() {
  const { render } = useBakery3(); // the provider's connection and scene
  // …
}

Add the dev panel

The dev panel lets you check your scene, see estimates and start renders and bakes while you build. Mount it next to the canvas. In React Three Fiber, give it your key or token as you give the hook, or nothing to use <Bakery3Provider>.

import { Bakery3Devtools } from '@oeave/bakery3/r3f';

{process.env.NODE_ENV === 'development' && <Bakery3Devtools token={getToken} />}

Devtools has every option.

Where to go next

  • Render image: size, format, quality and transparent backgrounds.
  • Render video: move the camera or your objects and get a clip back.
  • Bake a scene: keep the realistic lighting in your live, spinnable scene.
  • How it works: what the SDK sends when you render, and what it never sends.
  • Troubleshooting: the errors you are most likely to meet first.