OBS Layer
The layer surface renders inside OBS Browser Sources as a transparent overlay on the stream. It displays visual elements that appear over the live video feed.
Entry point
Every extension that includes the "layer" target must have a src/layer.tsx file:
// src/layer.tsx
import { Lumio, Text, Box, useExtensionStorage } from "@zaflun/lumio-sdk";
function ScoreOverlay() {
const [storage] = useExtensionStorage();
if (!storage.visible) return null;
return (
<Box
style={{
position: "absolute",
top: 20,
right: 20,
background: "rgba(0, 0, 0, 0.7)",
borderRadius: 8,
padding: 12,
}}
>
<Text
content={`${storage.homeTeam ?? "Home"} vs ${storage.awayTeam ?? "Away"}`}
variant="heading"
/>
<Text
content={`${storage.homeScore ?? 0} - ${storage.awayScore ?? 0}`}
variant="default"
/>
</Box>
);
}
Lumio.render(<ScoreOverlay />, { target: "layer" });
Transparency
The layer renders with a transparent background. The OBS Browser Source canvas is fully transparent by default — your component renders on top of the stream video feed.
- Use
position: absolutefor positioning elements anywhere on the canvas - The layer canvas is sized to the overlay dimensions (typically 1920×1080 for 1080p streams)
- Semi-transparent backgrounds (
rgba(...)) work for card-style overlays - Fully transparent areas pass through to the stream video
Read-only behavior
On the layer surface, input components are automatically disabled to prevent accidental interaction. They render in a read-only display mode:
| Component | Layer behavior |
|---|---|
Button | Not rendered (hidden) |
Toggle | Shows "On" or "Off" label text |
TextField | Shows current value as plain text |
TextArea | Shows current value as plain text |
NumberField | Shows current value as plain text |
Dropdown | Shows selected option label as plain text |
Slider | Shows current numeric value as plain text |
This is automatic — you do not need to conditionally render different components for the layer surface.
Responding to events
Use useLumioEvent() to react to live stream events and trigger animations or state changes:
import { Lumio, Box, Text, useLumioEvent } from "@zaflun/lumio-sdk";
import { useState, useEffect } from "react";
function FollowerAlert() {
const event = useLumioEvent("twitch:follower");
const [isVisible, setIsVisible] = useState(false);
const [followerName, setFollowerName] = useState("");
useEffect(() => {
if (event) {
setFollowerName(event.data.userName);
setIsVisible(true);
const timer = setTimeout(() => setIsVisible(false), 5000);
return () => clearTimeout(timer);
}
}, [event]);
if (!isVisible) return null;
return (
<Box
style={{
position: "absolute",
bottom: 80,
left: "50%",
transform: "translateX(-50%)",
animation: "slideUp 0.4s ease-out",
background: "linear-gradient(135deg, #6366f1, #8b5cf6)",
borderRadius: 12,
padding: "16px 24px",
}}
>
<Text content={`New follower: ${followerName}`} variant="heading" />
</Box>
);
}
Lumio.render(<FollowerAlert />, { target: "layer" });
Animations
CSS transitions and transforms are fully supported via the style prop:
<Box
style={{
transition: "all 0.3s ease",
transform: isVisible ? "translateY(0)" : "translateY(-20px)",
opacity: isVisible ? 1 : 0,
}}
>
<Text content="Score updated!" variant="heading" />
</Box>
Positioning strategy
Use position: absolute with top/right/bottom/left values (in pixels) to place elements. The layer canvas origin is the top-left corner:
// Top-right corner
<Box style={{ position: "absolute", top: 20, right: 20 }}>...</Box>
// Bottom-left corner
<Box style={{ position: "absolute", bottom: 80, left: 20 }}>...</Box>
// Centered horizontally at the top
<Box style={{ position: "absolute", top: 40, left: "50%", transform: "translateX(-50%)" }}>...</Box>
Performance considerations
The layer runs in a browser source — CPU/GPU usage affects stream quality. Keep the layer lightweight:
- Avoid heavy animations on elements that update every frame
- Use
if (!storage.visible) return null;to skip rendering when hidden - Prefer CSS transitions over JavaScript-driven animations
- Avoid rendering large numbers of DOM elements simultaneously