Skip to main content

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: absolute for 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:

ComponentLayer behavior
ButtonNot rendered (hidden)
ToggleShows "On" or "Off" label text
TextFieldShows current value as plain text
TextAreaShows current value as plain text
NumberFieldShows current value as plain text
DropdownShows selected option label as plain text
SliderShows 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