# Author a custom graphic in Studio

Write HTML, CSS and JavaScript beside a live preview, connect channel data, and share your design in a pack.

Updated 2 October 2026

## Before you start

Use a **Custom graphic** layer when you want your own layout, animation or canvas drawing inside an overlay. You need an overlay open in Studio and some HTML, CSS or JavaScript knowledge, or a [connected AI assistant](https://bloopbot.com/docs/connect-an-ai-assistant) to help write the source. Your graphic can fill the overlay or sit beside built-in layers.

Upload any pictures, sounds or videos to your [media library](https://bloopbot.com/docs/choose-and-upload-media) first. Declare any channel variables you want to display on **Variables**. This guide uses a raid card with a colour setting; it needs no media or variables.

## 1. Open code beside the preview

1. Open **Overlays**, then **Open in Studio** on your overlay.
2. Choose **Add a layer**, find **Custom graphic** under **Media**, and add it.
3. Select its layer and press **Edit graphic code** in the **Graphic** section.
4. Use the **HTML**, **CSS** and **JavaScript** tabs to change the card. The preview sits beside the code on wide screens and below it on narrow screens.

The starter already has a card, an **Accent colour** setting and a raid animation. For a first edit, change the text inside the HTML card or its padding in CSS. **Auto-run** refreshes the preview after a short pause in typing. Turn it off and choose **Run code** when you prefer to run changes yourself.

Changes save to your Studio draft. **Back to Studio** closes the workspace; Escape does the same. The draft does not reach OBS until you publish.

![Studio’s expanded code workspace with JavaScript on the left and a raid card, playback and event simulation on the right.](https://bloopbot.com/docs/screenshots/studio-graphics-code.jpg)

_JavaScript beside the graphic preview, with playback, a sample raid and Output._

## 2. Connect data and expose settings

Open **Data & settings** in the workspace.

1. Under **Libraries**, enable **GSAP** for animation or **PixiJS** for canvas/WebGL drawing. These are the versions included with Bloopbot.
2. Under **Events**, tick only the events your code reads. The starter selects **raid** and uses `bloop.onEvent("raid", handler)`.
3. Under **Variables**, select declared channel variables. Read their current values with `bloop.getVariables()` or receive updates with `bloop.onVariables(handler)`.
4. Under **Media**, give a library file a name such as `portrait`. Read its local URL as `bloop.assets.portrait`.
5. Under **Exposed settings**, add a text, colour, number, flag or select control. Read it as `bloop.settings.name`. These controls appear in the normal Studio panel, so someone adding your pack can customise it without editing code.

For example, a number variable named `wins` can fill a score:

```
bloop.onVariables(values => {
  document.querySelector("#score").textContent = String(values.wins ?? 0);
});
```

Add an element with `id="score"` to HTML and select **wins** under Variables first. Channel variable names use lowercase letters, digits and underscores. A flow can change that variable when a command or event happens; the graphic displays it.

![The custom graphic’s Data and settings tab shows bundled GSAP and PixiJS, permitted events and variable bindings beside the preview.](https://bloopbot.com/docs/screenshots/studio-graphics-bindings.jpg)

_Choose the bundled libraries, event subscriptions and channel variables in Data & settings._

The [SDK reference](https://bloopbot.com/developers/overlays#studio-graphics) covers every method, the manifest and limits. Source runs in its own sandbox and receives the data you selected. It cannot read the dashboard, cookies or storage. External script imports, fetch, sockets and nested players are blocked; bind library media instead. Named custom messages, leaderboard boards and Stage queues use the [separate HTML overlay SDK](https://bloopbot.com/docs/custom-widgets).

## 3. Test a chosen moment

1. Under **Simulate an event**, choose **raid**.
2. Leave the sample fields as `{"name":"Sample raider","viewers":42}` and choose **Send event**.
3. Press **Play preview** to watch the animation. **Pause preview** stops the shared clock.
4. Move **Preview time** to revisit a moment. The graphic reruns its code and replays the recorded events up to that time.
5. Choose **Reset preview** to clear sample events and start again. **Preview length** in Data & settings sets the time range, up to 60 seconds.

The name should change to _Sample raider_ and the card should animate. These events stay in the preview; they do not trigger Twitch, a flow or an OBS test. **Output** shows script and loading errors.

Use `bloop.timeline()` for GSAP animations, `bloop.schedule()` for delays and `bloop.onTick()` for canvas updates. CSS animations also follow the preview clock. Native timers and a library's independent ticker keep their own clock and cannot reproduce a paused or sought preview. A Pixi application should stop its ticker and render from `onTick`.

## 4. Publish and share

1. Choose **Back to Studio**, place and size the layer on the canvas, and check the whole overlay.
2. Press **Publish** when you want your existing OBS Browser Source to use the new graphic.
3. On the overlay's card, choose **Add to pack…** and select your pack or create one.
4. Share it using **Marketplace** or **Bundle file (.bloop.zip)**. A flow-only share link does not carry overlays.

The pack carries the graphic's source, controls, data declarations and bound media. Include the flows that drive its variables. A recipient gets their own media references and missing channel variable declarations; a compatible variable they already have keeps its value. Pack updates use the existing local-change review, so editing source or a control counts as a local change.

Only share source and media you have permission to distribute. Installing a graphic runs its JavaScript with the channel data its manifest selects. Review its source and declarations in Studio before publishing it to OBS.

## Ask an assistant to author it

Allow your connected assistant to read and edit overlays. Ask it to discover **graphics**, edit the complete Studio draft and render a preview before publishing. You can give it a concrete brief:

```
Create a custom graphic in Studio: a transparent raid card with an editable accent colour. Use the graphics layer and bundled GSAP. Subscribe only to raid. Render a preview at 500 ms with a sample raid from PixelPal, then leave the source in the draft for me to review.
```

An assistant can supply sample event fields, variable values and a timestamp for a rendered preview. Those are simulated values. If you change Studio while it works, its old draft revision is refused; ask it to reread the draft.

## If it does not look right

- **Blank preview:** read Output, check your element IDs, and choose Run code after fixing the source. An undeclared event or a timeline without GSAP reports an error.
- **A variable is missing:** declare it on Variables, select it in Data & settings and use its lowercase key. Viewer variables are unavailable to graphics.
- **Media is missing:** choose an existing library file for its binding. Graphics load at most 8 MB per file and 32 MB total; larger files need a smaller version even if the library accepted them.
- **Seeking differs from playback:** use the SDK clock methods and stop independent tickers. Preview history keeps the latest 256 events; reset before a fresh test.
- **A save is refused:** the workspace lists the setting field and reason. Repair the label, default, options or value before publishing.
- **OBS has the older graphic:** close the workspace and Publish the Studio draft. Keep the existing overlay Browser Source URL.
