# Generated once, rendered the same

Lolly uses AI in a few specific places: it can speak a script aloud, enlarge a small image and lift a subject off its background. This page is about how those features are built, because the pattern is the same for all of them and it is the interesting part. A model does one probabilistic thing, once, under guard-rails. Everything after that is a deterministic render that never guesses again.

That split is what makes a generated element safe to build on. The moment a voice clip or an enlarged photo exists, it stops being a model's shifting output and becomes a fixed artifact - a specific set of bytes. From then on every tool that uses it works from those same bytes, so the same input produces the same asset in the browser, in the terminal and on the desktop, today and next year. The unpredictable step is contained. It does not leak into the render.

![A generated storm image opened in Verify, its GEN AI badge beside the green Made with Lolly verdict and the credential-intact and bytes-unchanged checks](/t/url-shot?url=%2F%23%2Fverify%3Fsrc%3D%2Finfo%2Fthe-flood.webp&width=560&height=1400&dpi=192&waitMs=6000&waitSelector=.valid-steps&walker=1&format=svg&rasterDpi=96&cropSelector=.valid-result&css=.valid-panels%2C.valid-wm%2C.valid-note%7Bdisplay%3Anone%7D&dark=1&filename=aif-verify-genai)

## Two steps, and only one of them guesses

**Generation is the probabilistic step.** Asking a model to invent a voice or infer detail that was never in the pixels is, by nature, a guess. Lolly boxes that guess in: it runs on your own device with no upload, it happens as one explicit, cancellable action rather than inside a render loop and its output is disclosed for what it is. Then it is over. The result is saved as an ordinary asset - a clip, an image - and the model is not consulted again.

**The render is deterministic.** Every tool's entire state lives in its inputs, and those inputs live in the URL, so a link is a reproducible render (see [URL Mode](/info/build/url-mode.html)). The [CLI](/info/build/cli.html) is that same render path under a different transport: `--voice=…` on the command line is the same value the web shell reads from `?voice=…`. One path, so a browser and a build server cannot drift. Hand the same inputs to the engine and you get the same asset back.

This is why a generated ingredient is not a moving target. Re-open a project next year, render it in CI, paste the link to a colleague on another platform - the speech, the upscaled photo, the cutout all compose exactly the same way, because none of them is re-generated. It is also what provenance rests on. A Content Credential that attests to *these bytes* is only meaningful when those bytes are stable, and here they are stable by construction.

![A spoken clip turned into an audiogram, its waveform the actual shape of the audio - the kind of card a synthesised voice arrives in before it chains into a video's credentials](/t/url-shot?url=%2F%23%2Ftool%2Faudiogram%3Faudio%3Dlolly%2Floops%2Ffireplace-loop%26title%3DA%2520read%2520aloud%26subtitle%3DSynthesised%2520on%2520device%26style%3Dwave&width=1440&height=900&dpi=192&waitMs=3600&css=%23ag-wave%7Bdisplay%3Anone!important%7D.ag-ph%7Bdisplay%3Ablock!important%7D&walker=1&format=svg&rasterDpi=110&cropSelector=%23tool-canvas&dark=1&filename=aif-audiogram)

## The features, and how each is marked

- <!--i:neurobeat--> **Text to speech.** A tool's own script becomes a spoken clip on the device, offline, with no upload - a test pins that boundary (no remote models, same-origin runtime). The clip is generated once and saved as an AI-generated asset, so the [GEN AI badge](/info/trust/content-credentials-identity.html) surfaces wherever it appears; drop it into a video or an audiogram and that AI origin chains into the export's Content Credentials as an ingredient, so the composition declares it contains AI-generated audio. A visible line says so too, not only the manifest. We mark it even though today's synthetic voices are audibly robotic, because that tell is a property of this year's model, not a safeguard - the practice has to be in place before the tell disappears, and machine-readable disclosure of synthetic audio is what transparency rules like the EU AI Act ask for anyway. A voice is a style, not a person: it is credited as a model synthesised on device, never as a performer.
- <!--i:sparkle--> **An AI-enhanced image, from anywhere.** Whether Lolly enlarged it or you generated it in Gemini, Claude or OpenAI and brought it in, the moment the file enters Lolly its declared AI origin is read from its own credential and copied onto Lolly's manifest. Stripping the original's credential afterwards does not remove the flag, because it was carried forward at the instant of ingest. And from that instant it is a fixed asset, rendered identically by every tool.
- <!--i:cpu--> **Upscaling.** Enlarging a small raster invents high-frequency detail the original never held - it *adds* pixels. So an upscaled copy is disclosed as generative: a C2PA edit typed as `compositeWithTrainedAlgorithmicMedia` (a real photo with model-inferred pixels, never claimed as fully generated), flagged AI-generated and shown with the GEN AI badge on [Verify](/#/verify). It runs on-device, the weights download once and the image is never uploaded. The intent picker ("what are you upscaling?") routes to the right engine - and the pixel-art option is telling: it scales by nearest-neighbour, an algorithm rather than a model, so it copies existing pixels and is marked as a plain edit, *not* as AI. What earns the label is the operation, not the dialog it lives in.
- <!--i:seal--> **Background removal.** This is the counterpart, and the reason it shares a page with upscaling. Removing a background computes an alpha mask and keeps every original RGB pixel byte-for-byte; it *removes* data and adds none. So it does not get a GEN AI badge. It is disclosed honestly as an edit - a credential that records the operation (`Background removed with <model> <version>`) and keeps the original as an ingredient - but never as a generated composite, because nothing was generated. Where other removers strip your metadata, colour profile and credential to hand back a bare cutout, here all three stay with the cutout. It now runs on **video** too, saving an animated cut-out with real alpha beside the source. And the mask can come from a **colour key** rather than a model - a traditional key for footage shot against an evenly lit, flat backdrop - which uses no trained model, downloads nothing and is recorded the same plain way.
- <!--i:wrench--> **Retouch.** Painting out a blemish, a dust speck or a stray object runs a classical fill, pure maths, no model, entirely on-device. **Method** chooses how: **Content-Aware** copies texture from the nearby patch whose surroundings match best, **Proximity Match** prefers the closest such patch, **Create Texture** fills smoothly and adds grain like the detail around the spot, and **Smooth** is the Telea fast-marching fill that spreads nearby pixels inward along the mask's edge. Each copied patch is blended so that it meets the surrounding tone, and each separate painted area is healed on its own. None of them invents what was there. The result is a new asset carrying a plain `c2pa.edited` credential naming the operation (`Content-aware fill (on-device)`) with the original kept as an ingredient. It is not flagged AI-generated, because nothing learned is inventing pixels - the same stance as background removal.
- <!--i:document--> **Reading text out of pixels.** The on-device text reader (a recognition model, downloaded once) is behind **Read the text in this image** on [Verify](/#/verify), **Read text** in Assets, **Snap to text** in Annotate and **Suggest** in Redact. The image never leaves the device. It carries no badge and writes no credential at all, and the reason is the same mechanical test as everything above: reading produces no media - no pixel is invented, no pixel is even edited. What you do with the words afterwards is a separate act with its own record.

![Assets, where an image generated in Gemini, Claude or OpenAI and brought in sits beside the brand's own assets, each tile a permanent id every tool can call](/t/url-shot?url=%2F%23%2Fa&width=1440&height=1080&dpi=96&waitMs=2600&walker=1&format=svg&dark=1&filename=aif-assets)

## Add, or remove?

The line between "marked AI" and "an honest edit" is not a judgement call, it is a mechanical test: **did the model invent pixels that were not there?**

- <!--i:sparkle--> **Upscale invents detail** the low-resolution source did not contain. That is generative, so it carries the badge.
- <!--i:seal--> **A matte only selects** which existing pixels to keep. Nothing is invented, so it is an edit.
- <!--i:hash--> **Pixel-art scaling only copies** existing pixels to a larger grid. Also an edit.
- <!--i:seal--> **A colour key only keys out** a background colour you point it at, on footage shot for it. No trained model touches the pixels, so it is the plainest edit of the set.

Keeping the test this strict is what keeps the badge worth reading. It means "a model made up part of what you are looking at," not "a model was involved somewhere nearby." A badge that fired on every AI-adjacent operation - including ones that add nothing - would tell a viewer nothing, and over-claiming provenance is as dishonest as hiding it.

![The export panel's Content Credentials card, pre-ticked, so an AI-origin ingredient rides into whatever file the render becomes rather than being left behind](/t/url-shot?url=%2F%23%2Ftool%2Fqr-code%3Furl%3Dhttps%3A%2F%2Flolly.tools%26format%3Dpng%26options%26c2pa%3D90%26imprint%3D1&width=1440&height=900&dpi=192&waitMs=2400&walker=1&format=svg&css=%23tool-inputs%2C%23sidebar-utils%7Bdisplay%3Anone%7D&cropSelector=.export-c2pa&dark=1&filename=aif-export-cc)

## Check these claims yourself

Each claim here has a mechanism behind it, and each mechanism ships with tests: the on-device boundary, the source-type a given operation stamps and the flag that does or does not appear. Run any output through [Verify It Yourself](/info/trust/verify-yourself.html) and read the change history back for yourself. The engineering-level detail of these bridges lives in [Host API](/info/build/host-api.html), and the wider position they follow from is [Our AI Stance](/info/trust/ai-stance.html). If the code and this page ever disagree, the code is the bug.

---

Code ([MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/)) · Content ([CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)) · [lolly.tools](https://lolly.tools)
