Loading
Everything has its shape. The wait is the mark’s own.
How OpenVibe waits: the page arrives drawn, a list shows its own shape until it fills in, the mark works while a page moves, and a slow load says so. The same parts on every site, from openvibe-shared.
Placeholders shaped like the content
Seven shapes cover the network’s lists. Press Fill in: the real content takes the placeholder’s place, and the page says how far everything below it moved.
-
Card
A video or stream: its 16:9 picture and two lines.
Building a rocket from scratchiZoR_One · 1.2k watchingLate night chill and chatmia · 340 watchingSpeedrun practicekai · 88 watchingkit.skeleton({ kind: 'card', count: 3 }) -
Row
A person or a result: an avatar and two lines.
AAnaLive now · Just chattingMMiaOffline · 2h agoKKaiLive now · Gameskit.skeleton({ kind: 'row', count: 3 }) -
Tile
A square in a grid: an emote, an icon, a cover.
kit.skeleton({ kind: 'tile', count: 6 }) -
Chip
A tag or a filter.
MusicGamesArtTalkCodeIRLkit.skeleton({ kind: 'chip', count: 6 }) -
Line
Text that is on its way.
The first line of a paragraph that arrives a moment later.And another, a little shorter.A third, the shortest.kit.skeleton({ kind: 'line', count: 3 }) -
Chat
A chat message: a dot and a line.
mia that was so cleankai lol sameana welcome back!kit.skeleton({ kind: 'chat', count: 3 }) -
Stat
A number and what it counts.
1,284viewers today96%went smoothly12hstreamed this weekkit.skeleton({ kind: 'stat', count: 3 })
The mark at work
For a stream or a player that takes a moment: its own picture blurred behind, the V bobbing and the O breathing, one word of status and a line that fills with each step. Slow, it explains; failed, it says what happened.
const l = OpenVibeLoading.mount(player, { poster, label: 'Connecting', sub: 'Getting your viewer ready', steps: 4 });
l.update({ step: 1, label: 'Joining' });
l.slow({ hint: 'Still connecting', actions: [{ label: 'Try again', onClick: retry }] });
l.fail({ title: 'The stream ended', actions: [{ label: 'Retry', onClick: retry }] });
l.done();The rules
What every OpenVibe site does while you wait. Live’s measurements are in its performance audit.
- The page arrives drawn. Server-rendered HTML with its icons already drawn (no icon font to wait for), and only the CSS the first screen needs before it paints.
- A list shows its shape at once. Its placeholders are in the first HTML, the same size as what replaces them, so filling in moves nothing (each demo above measures it).
- A picture sits on its own colour first. Its dominant colour, or a tiny blurred copy, until it arrives. The first picture asks for itself first (fetchpriority="high"); the rest wait until they are near (loading="lazy").
- Moving between pages feels like one app. The mark works in the navbar with a two-pixel line under it. Links are fetched when pointed at (speculation rules), and the navbar and app bar stay still while the page cross-fades (view transitions).
- The wait is the mark’s own. Its O goes round as dots while it works, three dots rise inside it while an agent thinks, and a line fills when the length is known. Calm plays it slower; only motion off keeps it still.
- Slow says so. After a few seconds a load explains itself and offers what to do. A failure says what happened and offers Retry, never an endless wait.
- Off screen costs nothing. Long lists skip the work for what is not on screen yet (content-visibility: auto).
Use it
On an OpenVibe site the navbar brings all of it. Anywhere else, the files are free (MIT).
Placeholders, on the server
const kit = require('openvibe-shared/kit');
kit.skeleton({ kind: 'card', count: 6, label: 'Loading streams' });
// card · row · tile · chip · line · chat · statThe loader, in a page
<link rel="stylesheet" href="https://openvibe.design/shared/kit.css">
<script src="https://openvibe.design/shared/loading.js" defer></script>App-like page moves
<script src="https://openvibe.design/shared/boost.js" defer></script>
<meta name="ov-boost" content="mysite@1">