| name | design-obs-scene-pack |
| description | Generate an OBS scene pack — single 1920×1080 transparent HTML file with hash-routed scenes for additional broadcast layouts beyond the basic stream-overlay set. Scenes (#just-chatting, |
Skill: obs-scene-pack
Produces ONE self-contained obs-scene-pack.html file at 1920×1080 with transparent background and three hash-routed scenes designed for distinct broadcast contexts:
| Hash | Use | Treatment |
|---|
#just-chatting | host-on-camera segments (no game, no screen share) | Centered webcam frame + lower-third for show title + brand chrome at edges |
#fullscreen-game | gameplay or screen-share dominates | Minimal corner-only brand watermark (does not obscure content) |
#guest-split | host + guest both on camera | Two portrait frame zones side-by-side with named lower-thirds |
This skill EXTENDS the existing stream-overlay skill (which provides #starting / #brb / #webcam / #lower-third / #ending). Use them in combination for a complete broadcast workflow — stream-overlay.html for the per-stream chrome scenes; obs-scene-pack.html for the in-stream layout switches.
When to use
- User has a basic stream overlay set already (from
/stream-overlay) and needs additional in-stream layouts
- Stream format includes guest interviews, just-chatting segments, or gameplay
- A
DESIGN.md exists for the brand
Inputs
- Required: brand slug
- Required: show title / stream tagline (for the just-chatting lower-third)
- Required: streamer name + role/handle (for chrome on all scenes)
- Optional: guest scene defaults (placeholder names + roles for the split scene)
- Optional: webcam/guest frame dimensions if non-default
Output
./design/<brand-slug>/artifacts/obs-scene-pack.html (single file — overwrite OK; canonical scene-pack for the brand)
Steps
1. Verify brand exists
test -f ./design/<brand-slug>/tokens.css
2. Gather the brief
Ask in one message:
1. Show title / stream tagline (for just-chatting lower-third — e.g. "Live coding · payments stack")
2. Streamer name + handle (for chrome — e.g. "Shane Logsdon · @shanelogsdon")
3. Default webcam frame size: 480×270 (default) | 640×360 | 320×180
4. Guest split — default frame size: 720×405 each (16:9 each side); placeholder names for default rendering
3. Pick variation — applies to ALL scenes (consistency with existing stream-overlay set)
This pack must visually match the user's existing stream-overlay.html so the broadcast feels like one system, not separate sets:
chrome: bracketed (corner-frame L-marks — brand-canonical lineart) | chromeless (pure type with hairlines, no brackets)
mark-placement: matches existing stream-overlay (typically lower-left or lower-right; consistent across both files)
accent-usage: matches existing stream-overlay (e.g. amber pulse-dot on "live" indicator)
If the user has not run /stream-overlay for this brand, suggest running it first so the visual choices align.
4. Generate the HTML
Template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><Brand> — OBS scene pack</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=<families>&display=swap">
<style>
<contents of tokens.css>
html, body { margin: 0; padding: 0; width: 1920px; height: 1080px; overflow: hidden; background: transparent; font-family: var(--type-sans-family); color: var(--color-ink); }
.scene { position: fixed; inset: 0; width: 1920px; height: 1080px; display: none; }
.scene.is-active { display: block; }
.mark-corner {
position: absolute;
bottom: 64px; left: 80px;
display: inline-flex; align-items: center; gap: 10px;
font-family: var(--type-display-family); font-weight: 600; font-size: 22px;
letter-spacing: -0.005em; color: var(--color-ink);
background: var(--color-surface); padding: 6px 12px;
border: 1px solid var(--color-rule);
}
#scene-just-chatting .topbar {
position: absolute; top: 64px; left: 80px;
display: flex; flex-direction: column; gap: 12px;
}
#scene-just-chatting .topbar .label {
font-family: var(--type-display-family); font-weight: 600;
font-variant-caps: small-caps; font-feature-settings: 'smcp';
text-transform: lowercase; font-size: 18px; letter-spacing: 0.1em;
color: var(--color-ink); background: var(--color-surface); padding: 8px 14px;
border: 1px solid var(--color-rule);
}
#scene-just-chatting .cam {
position: absolute;
top: 50%; left: 50%; transform: translate(-50%, -45%);
width: 720px; height: 405px;
background: transparent;
border: 1px solid var(--color-rule);
}
#scene-just-chatting .cam::before, #scene-just-chatting .cam::after {
content: ""; position: absolute; width: 14px; height: 14px;
}
#scene-just-chatting .cam::before { top: -1px; left: -1px; border-top: 1px solid var(--color-ink); border-left: 1px solid var(--color-ink); }
#scene-just-chatting .cam::after { bottom: -1px; right: -1px; border-bottom: 1px solid var(--color-ink); border-right: 1px solid var(--color-ink); }
#scene-just-chatting .lower-third {
position: absolute;
bottom: 96px; left: 80px; right: 80px;
display: grid; grid-template-columns: auto 1fr auto;
gap: 32px; align-items: baseline;
background: var(--color-surface);
padding: 16px 24px;
border: 1px solid var(--color-rule);
}
#scene-just-chatting .lower-third .name {
font-family: var(--type-display-family); font-weight: 500;
font-size: 32px; letter-spacing: -0.008em; color: var(--color-ink);
}
#scene-just-chatting .lower-third .role {
font-family: var(--type-mono-family); font-size: 14px;
letter-spacing: 0.06em; color: var(--color-ink-3);
}
#scene-just-chatting .lower-third .handles {
font-family: var(--type-mono-family); font-size: 14px;
letter-spacing: 0.06em; color: var(--color-ink-3);
}
#scene-fullscreen-game .corner-watermark {
position: absolute;
bottom: 32px; right: 40px;
display: inline-flex; align-items: center; gap: 8px;
font-family: var(--type-display-family); font-weight: 600;
font-size: 16px; letter-spacing: -0.005em;
color: var(--color-ink);
background: var(--color-surface);
padding: 6px 10px;
border: 1px solid var(--color-rule);
opacity: 0.85;
}
#scene-guest-split .topbar {
position: absolute; top: 56px; left: 80px; right: 80px;
display: flex; justify-content: space-between; align-items: baseline;
background: var(--color-surface);
padding: 10px 16px;
border: 1px solid var(--color-rule);
font-family: var(--type-display-family); font-weight: 600;
font-variant-caps: small-caps; font-feature-settings: 'smcp';
text-transform: lowercase; font-size: 14px; letter-spacing: 0.1em;
color: var(--color-ink);
}
#scene-guest-split .frames {
position: absolute;
top: 50%; left: 50%; transform: translate(-50%, -45%);
display: grid; grid-template-columns: 720px 720px;
gap: 32px;
}
#scene-guest-split .frame {
width: 720px; height: 405px;
background: transparent;
border: 1px solid var(--color-rule);
position: relative;
}
#scene-guest-split .frame::before, #scene-guest-split .frame::after {
content: ""; position: absolute; width: 12px; height: 12px;
}
#scene-guest-split .frame::before { top: -1px; left: -1px; border-top: 1px solid var(--color-ink); border-left: 1px solid var(--color-ink); }
#scene-guest-split .frame::after { bottom: -1px; right: -1px; border-bottom: 1px solid var(--color-ink); border-right: 1px solid var(--color-ink); }
#scene-guest-split .frame .label {
position: absolute; top: 100%; left: 0;
margin-top: 12px;
background: var(--color-surface);
padding: 6px 10px;
border: 1px solid var(--color-rule);
font-family: var(--type-mono-family); font-size: 13px; letter-spacing: 0.06em;
color: var(--color-ink);
}
#scene-guest-split .frame .label .role {
display: block; margin-top: 2px; font-size: 10px;
text-transform: uppercase; letter-spacing: 0.18em; color: var(--color-ink-3);
}
</style>
</head>
<body>
<section id="scene-just-chatting" class="scene">
<div class="topbar">
<span class="label">— live · just chatting</span>
</div>
<div class="cam"></div>
<div class="lower-third">
<span class="name">[Streamer name]</span>
<span class="role">[show title / topic]</span>
<span class="handles">[handle / handle]</span>
</div>
</section>
<section id="scene-fullscreen-game" class="scene">
<span class="corner-watermark">[Wordmark]</span>
</section>
<section id="scene-guest-split" class="scene">
<div class="topbar">
<span>— live · guest interview</span>
<span>[show title]</span>
</div>
<div class="frames">
<div class="frame">
<span class="label">[Host name]<span class="role">host</span></span>
</div>
<div class="frame">
<span class="label">[Guest name]<span class="role">guest · [role]</span></span>
</div>
</div>
</section>
<script>
function activateScene() {
var hash = (window.location.hash || '#just-chatting').slice(1);
var scenes = document.querySelectorAll('.scene');
var matched = false;
scenes.forEach(function(el) {
var isMatch = el.id === 'scene-' + hash;
el.classList.toggle('is-active', isMatch);
if (isMatch) matched = true;
});
if (!matched) document.getElementById('scene-just-chatting').classList.add('is-active');
}
window.addEventListener('hashchange', activateScene);
activateScene();
</script>
</body>
</html>
5. Verify
OBS scene pack at ./design/<brand-slug>/artifacts/obs-scene-pack.html.
Variation: <chrome> × <mark-placement> × <accent-usage>.
OBS setup: Add as a Browser Source → Local file → 1920×1080 → URL with #just-chatting (or other scene). Create one OBS scene per hash, all pointing to the same file with different URL hash. The transparent background composites over your video / game capture / guest layout.
Anti-Slop Rules
Governed by three shared canonical references — read them, do not restate them:
../design-anti-patterns.md — the hard floor. Wins every conflict.
../design-principles.md — the craft floor (typographic hierarchy, spatial rhythm, color theory, layout logic, visual tension).
../design-variation-sop.md — the named-aesthetic roster + the procedure for varying every invocation.
Forbidden (fast scan — the references hold the full list):
- Indigo/violet/purple as default accent or primary — the
hsl(230–280) family on a near-white surface. The single loudest AI tell.
- Purple→blue / "aurora" / gradient-mesh backgrounds; gradient-fill text.
- Glassmorphism without an explicit AA-contrast scrim; floating gradient "blobs" as atmosphere.
- The three-up icon-card feature grid; the "hero → 3 cards → CTA" median skeleton.
- One global border-radius on every element; timid evenly-weighted low-contrast palette.
- Inter / Roboto / Arial / Open Sans / Lato / Helvetica / system as the primary family (Space Grotesk = yellow flag). Use a category-matched stack from the variation roster.
- Value-free CTAs ("Get Started", "Learn More", "Sign Up"); the two-CTA hero. Name the real action and its value.
Required variation (every invocation):
- Make at least two intentional decisions that differ from the safe defaults (palette, layout structure, typographic voice, or spatial density) AND from the most recent artifact in this brand.
- Never default to "clean / minimal / modern" — that is the absence of a direction (variation-sop Rule 1).
- Sketch three distinct directions, offer them as a one-line menu, generate the best-fit by default, all three only if asked (variation-sop Rule 3).
Authenticity: prefer specific over generic everywhere — real datelines (not "today"), the brand's actual voice (not "build the future" filler), concrete CTAs over placeholders.
Rules
- Single file always. OBS loads one URL per browser source; multiple files defeat the purpose of hash routing.
- Transparent background — hard requirement. Scene compositing breaks without it.
- Cam frames stay transparent. They're cutouts for live video to show through. The brand chrome wraps around them, never inside them.
- Backed cream surfaces for any text overlay. Text on transparent over arbitrary video is unreadable; lower-thirds and top-bars need a 1px-bordered cream backing for legibility.
- No animations. This pack is for steady broadcast layouts; alerts go in
obs-alert-overlay.
- Token-pure. Every visual value via
var(--*) from tokens.css.
- Anti-pattern compliance. This skill defers to three shared canonical references:
../design-anti-patterns.md (the hard floor — wins every conflict), ../design-principles.md (the craft floor), and ../design-variation-sop.md (direction roster + offer-3 procedure). The anti-patterns file is the canonical anti-tell list.