Atmosphere✨ Premium

Reaction Burst

Emoji reaction bar for live streaming: per-particle cubic Bézier path, lateral sine wobble, recycled DOM pool (max 40), and a simulated viewer feed — no canvas, native prefers-reduced-motion support.

EmojiLive ReactionsBézierDOM Pool

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Atmosphere has 57 effects, including 10 free. Explore the category →

3 usage examples

Hero / Landing — Live webinar landing page — Reaction Burst example 1

① Hero / Landing — Live webinar landing page

WhenA live webinar or masterclass is promoted from the landing page — the page needs to convey the energy of a real live event before the visitor clicks.
WhyThe reaction bar with the LIVE badge and simulated feed creates instant social proof. The visitor perceives an already-animated audience, reducing sign-up hesitation.
SettingssetLiveSpeed(3) for a fast feed (200–800 ms), setBurst(4) for 4 particles per user click, high initial counters (≥ 1 000) to signal an active community.
Product component — Session engagement card in a dashboard — Reaction Burst example 2

② Product component — Session engagement card in a dashboard

WhenA live platform dashboard (OBS, StreamYard, Twitch-like) shows a card summarizing session reactions in real time.
WhyThe effect puts raw metrics into context: particles animate reaction spikes without overwhelming the key figures displayed in the card. The simulated live feed can be replaced by real WebSocket events.
SettingssetLiveSpeed(1) (1 800–4 000 ms interval) for a readable cadence, setBurst(2) for 2 particles per click, realistic initial counters (100–500 depending on topic).
Live reaction wall — keynote or sports broadcast — Reaction Burst example 3

③ Live reaction wall — keynote or sports broadcast

WhenAn event is broadcast live (tech conference, sports final, concert). Viewers send reactions continuously during highlight moments.
WhyThe stage's full height simulates a rising reaction wall: Bézier trajectories spread freely across the large surface, making every particle fully visible. The speed-3 simulated feed faithfully reproduces the intensity peak of a highlight moment.
SettingssetLiveSpeed(3) for ultra-fast flow (200–800 ms) matching a reaction spike, setBurst(4) for 4 particles per click, high initial counters (≥ 2 000) to reflect a popular event.

How it works

The effect creates one RBXScene instance per .rbx-scene element found in the DOM. Each instance manages a pool of reusable <span> elements (max 40 active at once): when a span completes its trajectory, it is pushed back into the pool rather than removed — zero element creation after the first wave.

On each click or Enter/Space keypress, react() computes the start position from the stage's bottom edge (client → local), then calls _launch() for each particle in the burst. The trajectory is a 1D cubic Bézier (bz(t, p0, p1, p2, p3)) applied independently to x and y — the four control points are randomized per launch. A sine wobble (Math.sin(t × π × wFreq) × wAmp) is added to x for lateral oscillation. Opacity stays flat from 0 to 0.55 then fades; scale grows from 0→0.12, plateaus, then decreases slightly toward the end.

A simulated live feed runs in parallel via a self-rescheduling setTimeout (random interval 600–2 600 ms by default). It picks a random emoji, increments its counter, and launches a particle from the right third of the stage to mimic other viewers reacting. Counters are formatted by fmt(n): integers below 1 000, one decimal between 1 000 and 9 999 (1.2k), integer above 10 000 (12k); aria-live="polite" elements relay updates to screen readers.

The rAF loop iterates _active in reverse and removes particles that reach t = 1. If the pool is full when a new particle launches, the oldest entry is evicted (_active.shift()) — a hard cap of 40. The public API window.RBX_0839.scenes[i] exposes setBurst(n), setLiveSpeed(preset), fireAll(), and resetCounts() to control each scene independently.

Accessibility

  • prefers-reduced-motion: detected at startup via matchMedia. In reduced mode, CSS disables the rbx-bumping scale animation and the live-dot pulse; JS fires only 1 particle per click (instead of 3), duration 420 ms, in a straight line (linear interpolation, no Bézier). The live feed keeps incrementing counters but launches no particles.
  • Each .rbx-btn is a native <button> with a descriptive aria-label ("Like", "Heart"…). JS binds keydown for Enter and Space — the effect is fully operable by keyboard.
  • .rbx-btn-count and .rbx-total-count carry aria-live="polite": screen readers announce each counter update without interrupting ongoing reading.
  • Flying particles (.rbx-particle) carry aria-hidden="true" and are excluded from the tab order — they do not interfere with keyboard navigation or screen readers.
  • The focused button receives a visible :focus-visible orange ring (outline-color: #f59e0b) on dark backgrounds (contrast ratio ≥ 3:1 against #0a0a0f). The button group is annotated role="group" aria-label="Reactions".

Browser compatibility

Requires modern DOM, requestAnimationFrame, and classList — no canvas, no external dependencies.

Chrome 80+✓ Full
Firefox 75+✓ Full
Safari 14+✓ Full
Edge 80+✓ Full
Mobile iOS✓ Touch OK
Android Chrome✓ Full

With JS disabled, the reaction bar remains visible as plain HTML with its static initial counters — no errors, the page stays functional.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="rbx-scene" role="region" aria-label="Live reaction bar — click an emoji to send a reaction">

  <!-- Particle flight zone + visual background + LIVE badge -->
  <div class="rbx-stage">
    <div class="rbx-video-bg" aria-hidden="true"></div>

    <div class="rbx-live-badge" aria-hidden="true">
      <span class="rbx-live-dot"></span>
      <span class="rbx-live-text">LIVE</span>
      <span class="rbx-viewer-count">1,284 viewers</span>
    </div>

    <!-- Free slot: thumbnail, title, player… -->
    <div class="rbx-video-meta" aria-hidden="true">
      <div class="rbx-video-icon">…</div>
      <span class="rbx-video-title">Live title</span>
    </div>
  </div>

  <!-- Reaction bar -->
  <div class="rbx-bar" role="group" aria-label="Reactions">
    <button class="rbx-btn" data-initial="847" aria-label="Like">
      <span class="rbx-btn-emoji" aria-hidden="true">👍</span>
      <span class="rbx-btn-count" aria-live="polite">847</span>
    </button>
    <!-- repeat for each emoji -->

    <div class="rbx-total" aria-live="polite" aria-label="Total reactions">
      <span class="rbx-total-label">Total</span>
      <span class="rbx-total-count">3.1k</span>
    </div>
  </div>

</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
data-initial="n" varies per button Starting counter value for each .rbx-btn. Set it to display realistic social proof from page load.
RBXScene.setBurst(n) 3 Number of particles fired per user click. 1 = subtle, 3 = default, 5–6 = festive. Apply per scene via window.RBX_0839.scenes[i].setBurst(n).
RBXScene.setLiveSpeed(preset) 2 (600–2 600 ms) Simulated feed cadence: 1 = slow (1 800–4 000 ms), 2 = normal, 3 = fast (200–800 ms). Tune to match the desired perceived pace.
RBXScene.fireAll() Fires one reaction from each of the 5 emojis in a cascade (90 ms apart). Best on business events: payment confirmed, sign-up validated, milestone reached.
RBXScene.resetCounts() Resets all counters to their data-initial value. Useful to reinitialize the demo between tests or sessions.
MAX_P (JS constant, line 6) 40 Hard cap on the active particle pool. Drop to 20 for low-end devices, raise to 60 for denser effects — beyond the cap the oldest particle is evicted.
.rbx-scene.rbx-hc absent CSS class that switches the bar and buttons to high-contrast mode (background #111118, borders at 40% opacity, white text). Toggle via JS based on detected accessibility needs.
Flight duration (JS, _launch function) 1 100–1 800 ms Constant 1100 + Math.random() * 700 inside _launch(). Lower to 600 + Math.random() * 300 for snappy reactions, raise for a more contemplative float.

FAQ

No canvas at all: each particle is an absolutely-positioned <span> inside the .rbx-stage. The transform and opacity properties are updated every frame via requestAnimationFrame. This simplifies integration in any framework (React, Vue, Svelte) and makes particles inherently accessible via aria-hidden.
The simulated feed runs inside _startLive() via a setTimeout. To replace it, simply call scene.react(emojiIndex, clientX, clientY) from your WebSocket listener — emojiIndex runs 0 (👍) to 4 (👏). The pool and the 40-particle cap apply identically.
The list lives in the EMOJIS constant at the top of the IIFE (['👍','❤️','🎉','🔥','👏']). It must stay in sync with the .rbx-btn elements in the HTML (same order, same length): button at index i maps to EMOJIS[i]. Edit both together to swap the emoji set.