Cost & estimates

Call estimate() to see what a render will cost before you start it. Set maxCost, and a render that could cost more is refused before anything is charged.

Get the price before you render

const cost = await bakery3.estimate({ quality: 'studio', width: 2048, height: 2048 });

cost.estimatedCost;        // 0.12, expected charge
cost.maximumCost;          // 0.16, the most you can be charged
cost.estimatedSeconds;     // 7
cost.compatibilityScore;   // 0.97

maximumCost is 35% above the estimate, because a noisy scene can take longer than expected. You are never charged more than the maximum. The more your project renders, the closer its estimates come to what it is charged.

Set a budget with maxCost

await bakery3.render({ maxCost: 0.14 });

maxCost is compared with maximumCost, not with estimatedCost. If the maximum is higher, the render is refused and nothing is charged:

RenderBudgetExceeded: This render could cost up to $0.16, which exceeds maxCost $0.14 (estimated $0.12)

Why: maxCost is held against the maximum a render can be charged,
     not its estimate, so that it is a ceiling. The render was
     refused before any GPU work started, and nothing was charged.
Fix: Raise maxCost, lower the quality preset, or reduce the output
     resolution.

Catch it to offer your user something cheaper:

import { RenderBudgetExceeded } from '@oeave/bakery3';

try {
  await bakery3.render({ maxCost: budgetRemaining });
} catch (error) {
  if (error instanceof RenderBudgetExceeded) {
    // number | undefined: a refusal from a browser token's own cap names no price
    return offerCheaperPreset(error.maximumCost);
  }
  throw error;
}

The refusal can come from your maxCost, which the SDK checks before it uploads anything, or from the API, for example when a browser token has less left than the render could cost. Both are instanceof RenderBudgetExceeded. maximumCost, estimatedCost and maxCost on it are set when they are known.

Set a default once and every render uses it:

createBakery3({ token, defaults: { quality: 'studio', maxCost: 0.5 } });

defaults.output sets a size for every render, and estimate() quotes at that size too, so the price you show is the price of the render that follows.

See what you were charged

const result = await job.result();

result.cost;        // 0.13, what was charged, not the estimate

You are charged for the render that actually ran, and never more than the maximumCost it was accepted under.

What drives the price

The price follows the output pixels, the quality preset and how big the scene is. A scene inside a room costs more than one on an open set, and a room you modeled yourself is priced the same way as a preset room. Doubling the long edge gives four times the pixels and roughly four times the price. Dropping from ultra to studio is the biggest single saving.

previewstudioultra
Max samples322562048
Adaptive threshold0.080.020.005
Max bounces4816
Diffuse / glossy / transmission2 / 4 / 43 / 8 / 86 / 16 / 16
Default size, long edge1024 px2048 px4096 px
Quick preview firstnoyes, 24 samplesyes, 32 samples
Denoisednoyesyes

A preview costs at least $0.04, a studio render at least $0.08 and an ultra render at least $0.20. Every render also has a small fixed start-up cost, so a tiny preview and a small studio image are closer in price than their pixel counts suggest.

The same render twice is free

A render that comes from the cache costs nothing, as long as the first one is still stored. Results are kept for 30 days by default. See Assets & caching.

When your balance is too low

Rendering is prepaid. A render is only accepted if your credit covers its maximumCost, so your balance cannot go below zero.

InsufficientBalance: This render could cost up to $0.16 and you have $0.10 available. Top up at https://bakery3.com/billing.

You can also set a hard monthly limit on the account and on each project. See Limits.

Estimate a video

Pass the frame count:

const cost = await bakery3.estimate({
  quality: 'studio',
  width: 1920,
  height: 1080,
  frames: 96,
});

A video pays the start-up cost once, and each frame costs about a fifth of an image the same size. maxCost on renderVideo() is checked against the video's maximum. See Render video.

Estimating a bake

Pass the same settings you would give bake(). The quote covers exactly the objects the bake would include.

const cost = await bakery3.estimate({
  bake: { quality: 'standard', textureSize: 1024, exclude: ['glass-vitrine'] },
});

A bake is priced at twice the rate of an image per GPU second, and maxCost works the same way. This is what moves the price:

WhatHow it changes the price
TexelsEvery included object at its texture size, squared. Half the texture size is a quarter of the texels.
Qualitypreview traces a quarter of the samples of standard, and high four times as many.
Number of objectsEach lightmap has a fixed cost, so many small maps cost more than a few large ones with the same texels.
Scene weightWhat a sample costs is set by the scene the light bounces around in, so a heavy room costs more per texel than a bare one. This is usually the largest number in the quote.
Geometry you bakeThe unwrap is priced per triangle of the objects you include, not of the whole scene.
deliverGlbThe portable .glb is a second pass per object and roughly doubles the price.
atlasCosts the same as separate maps with the same texels. Its texels are size squared.

The quote prices every object at its full texture size. We may choose a smaller lightmap for a small object, so the charge can come in under the quote. Baking the same scene again from the same camera with the same settings comes from the cache and is free, while the first bake is still stored.

Estimates are fitted against real bakes and are within about a third either way. Whatever happens, you pay the metered price or maximumCost, whichever is lower.

See Pricing for example bake prices, and Bake a scene for the settings.

In the dev panel

The dev panel shows the estimate next to the render button and updates it when the scene changes. See Devtools.