Docs
Everything here is short on purpose — the studio is the manual. Poke things; ⌘Z — Ctrl Z on Windows — always brings the look back.
The studio in a minute
Or take the five-step walkthrough instead: it points at the real interface, in the order you use it, and it's under Help (the ? in the corner of the stage) → Take the tour.
The left rail is the tool library: the layers (Color, Image, Pattern) and 23 effects, grouped into families — Distort, Light & lens, Color & tone, Pixels & print, Texture, Interact. Click one, or drag it onto the canvas, to add it to the composition. It starts as a column of icons — hover one to hear its name — and the chevron at the top opens the full named list when you'd rather read it than reach for it. The right panel shows a section for everything that's in, ordered the way it renders: higher in the panel means later in the render, so the top section sits on top of the picture.
The whole composition is one stack — layers included. Every section has a ⠿ handle to re-stack it by drag, an eye to hide it without losing its settings, and a right-click (or ⋮) menu to hide, lock, re-roll or remove that one member — the roll there deals a fresh set of settings for that section alone and leaves the rest of the composition standing. A stage only ever affects what's below it, Photoshop-style: drag your photo above Halftone and it rides crisp over the halftoned color underneath; drop it back below and the dots print over everything. Glass above halftone refracts the printed dots; halftone above glass prints the refraction. A fresh effect arrives as the only open section — everything else folds shut, so the controls under your cursor are the ones you just added. Click a title to open or shut a section; it stays that way, reload included. Many sections keep their deepest controls in an Advanced fold at the bottom; it opens itself whenever the look already leans on one of them.
Every color in the studio — a palette stop, an ink, a backdrop — opens the same picker: a square, a hue strip, a hex field, and a dropper for taking a color by pointing at it. How far that dropper reaches is the one thing in the studio your browser decides. On Chromium browsers it opens the browser's own screen magnifier and reads any pixel you point at, inside this page or outside it. On Firefox and Safari, which have no such magnifier, it reads the canvas instead — a loupe follows your cursor over the artwork showing the pixels under it and the hex you're about to take. Either way you get the color that's really on screen, after every effect in the stack, so sampling your own gradient can hand back a value that's in none of its stops. The whole visit to a picker is one undo step however many colors you try inside it, and Escape puts back the one you opened on.
Every look is a link
The whole design packs into a link — there is no account and no save button. Export → Copy look (C) puts a shareable link on your clipboard; anyone who opens it gets your exact composition, sliders and all. It sits with the other exports because that's what it is — the cheapest one, and the only one that stays editable.
While you edit, the address bar stays clean — no history entry per slider nudge — and the studio remembers your working look on this device, so a plain reload picks up right where you left off.
One caveat: uploaded photos stay on your device, so links can't carry them. The bundled photos travel fine.
Randomize
Space rolls the dice. The scope picker next to the die chooses how far a roll reaches: Light re-rolls what's already in the composition, Full may add and remove effects too. Your photo is never touched, and anything locked (right-click a section) stays exactly as it is — settings, and whether it's showing. Hiding a stage isn't a lock: a Light roll leaves it alone, since it only re-rolls what's on screen, but a Full roll is a new composition, so anything it deals comes back visible. A Full roll folds the whole panel shut, so what you read after one is the stack it dealt — one line per stage, in order.
Pointer reactivity
Pointer is a stage in the effect stack, not a global setting. It only moves what's below it — so with Glass above Pointer, the flutes stay still while the content behind them bulges toward the cursor; drag Pointer to the top and the whole print bends instead. Four reactions, every one of them a distortion: a warp bulge, a parallax drift, a vortex swirl that stirs the print around the cursor, and a spring that turns the print into particles — marks flee the cursor with real momentum, then snap back home on a spring, overshooting once before they settle. ASCII under Spring goes furthest: every glyph is a true particle, so a fleeing crowd leaves a real gap behind and piles up where it lands. With Pixelate, Halftone or Dither below it the blocks and dots fly whole on their own grid, and over a plain print the sheet flexes like rubber. Stiffness sets the personality of the return — low is loose and floaty, high snaps tight. Every reaction also runs the other way: reversed, the bulge pinches instead of magnifying, the drift leans in, the swirl turns against the cursor, and the spring gathers marks rather than scattering them.
Other things follow on their own, no stage required. The Color layer's Follows the pointer hands one of your palette's colors to the cursor. By default the palette chooses — the lightest stop, the one that shines through whatever is stacked above, re-chosen whenever you edit the stops — and tapping the swatch beside the switch opens the layer's own stops, shown as ink, to hand the ride to any stop instead. On Mesh that stop's own point swims over to it, and on every other shape the chosen color blooms through the field around the cursor — the field warps around the travelling color and blends out through its neighboring stops at the rim. Because it's the layer moving underneath, effects stacked above keep their marks planted and simply re-ink the passing color: the halftone grid stays put while its dots change hue.
The Light stage follows too, and there it's the light source that rides: switch on Follows the pointer and the light travels with the cursor carrying its own effect, color, amount and size — so it can drag tinted caustics or a fan of rays around, not just a glow. Light more than one source and a row appears to hand the ride to another. It fades in as the cursor arrives and out as it leaves, back to the position on the pad.
Stills are always neutral: PNGs, videos and thumbnails render the resting look no matter where the mouse was.
Animation
One key (A), or the play button under the stage, sets the whole composition in motion, and every stage carries its own: color drifts, waves travel, light shimmers, glass gently swims, halftone dots breathe, ASCII glyphs flicker, film grain and the noise dithers boil. The grids themselves hold still — the pixel blocks, the ordered dither's weave, halftone's cells and ASCII's glyph cells all stay exactly where you set them, and change because the picture moves underneath them. It all rides one shared cycle, so exported loops close seamlessly at whatever speed you run them — and none of it touches your stills: with animation off, the look renders exactly as designed.
Export
The Export button (or E) offers PNG at HD, 2K or 4K — always in the Frame's aspect ratio, and each row spells out the pixels it will save — and a seamless-loop video once Animation is on (4–12 s, your pick) or a video layer is present (the export matches the footage's length, so the wrap lands exactly on the file boundary). Browsers with WebCodecs get an H.264 MP4, rendered offline as fast as your GPU draws; everywhere else the loop records as WebM in real time. Either way the cycle ends exactly where it began.
Embed a look in your site
Export → Copy embed code hands you a two-line component: one <script> tag for the hosted runtime — cached once, shared by every look on your site — and an <oddity-bg> element carrying your look as the same hash you see in the studio URL. Different look? Change the look attribute; the runtime stays cached.
- Design the look; add a Pointer stage if you want it to react.
- Export → Copy embed code.
- Paste it inside any section with
position: relative— the component fills it and sits behind its content.
The component tracks the cursor on the parent section, so it reacts through your headline without ever eating a click. It pauses offscreen and in hidden tabs, renders a static frame under prefers-reduced-motion, and its comment header links back to the editable look in the studio. Bundled textures load from Oddity; a photo you uploaded travels inline as a data URI, capped at 1920 px. Download test page gives you the same snippet inside a ready-made hero to try locally.
Footage is the one source the code can't contain — a clip as a data URI would be a multi-megabyte attribute — so the export hands you the file instead. On a look with a video layer the rows read Copy embed code + clip: you get the snippet and the clip in the same click, and the element carries a video attribute naming it.
<oddity-bg
look="v=8&fx=Ilar&..."
video="./oddity-clip.mp4"
></oddity-bg>The path is resolved against your page, not against Oddity: keep the file beside the page, or point video at wherever you serve it. Serving it from another origin needs Access-Control-Allow-Origin — the frames become a WebGL texture, and a tainted one can't be uploaded. Footage counts as motion the way animation does: it holds on its first frame under prefers-reduced-motion, and it parks offscreen and in hidden tabs rather than decoding to nobody.
Heads-up: some machines switch animations off at the OS level — Windows' Animation effects setting, macOS's Reduce Motion — and every browser on them then reports prefers-reduced-motion. On those machines the embed (and the hero on this site) shows its static frame and skips the pointer reaction. That's the component respecting the viewer's preference, not a bug — design looks that also hold up as stills.
Use a look in an Angular app
Export → Copy Angular component hands you a ready component built on the oddity-angular package: a <oddity-canvas [config]> element driven by a typed IOddityConfig — every field of the look, spelled out and editable in your codebase instead of packed into a hash. The package's renderer is generated from the studio's own modules, so the component draws exactly what the studio shows.
npm install oddity-angular(Angular 22 or newer).- Export → Copy Angular component, paste the file in.
- Drop the selector inside any section with
position: relative— like the embed, it fills the section, sits behind your content, and never eats a click.
The config carries only what differs from the studio defaults, so a look reads like a recipe. Change any field and the canvas re-renders; pointer reactivity, animation and reduced-motion behavior match the embed exactly. Photos you uploaded travel inline as data URIs. Footage takes the embed's route in config form — the clip saves with the snippet, and imageSrc: "./oddity-clip.mp4" with imageKind: "vid" points at a file your app serves. React and Vue flavors are on the roadmap; the embed covers every framework in the meantime.
Handing the file to a coding agent works, and it has somewhere to look things up: /llms.txt is this page's machine-readable half — every field of IOddityConfig with its default, grouped by the stage that owns it, generated from the same definitions the studio renders from. An agent that reads it can change a look on instruction instead of guessing at field names.
Shortcuts
Keyboard
- Randomize
- Space
- Undo
- ⌘ZorCtrl Z
- Redo
- ⌘⇧Z / ⌘YorCtrl ⇧Z / Ctrl Y
- Copy look
- C
- Export menu
- E
- Toggle animation
- A
- Nudge the seed
- ← →
- Shortcuts panel
- ?
Mouse & trackpad
- Zoom the stage
- Scroll / pinch
- Pan the canvas
- Drag
- Reset zoom & pan
- Double-click
- Add it to the composition
- Click / drag a tool
- Hide · lock · randomize · remove
- Right-click a section
- Reorder the effect stack
- Drag ⠿
Sliders & pads
- Type an exact value
- Click the number
- Move ten steps at once
- ⇧ drag or arrows
- Slip past the default
- ⌥ dragorAlt drag
- Back to the default
- Double-click