block / block/buzz

perf(buzz.xyz): landing page — cursor lag, hero text glitch on scroll, WebGL main-thread contention

Open
#2,291 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

## Summary

The marketing landing page at **https://buzz.xyz/** feels laggy on mid/low-end
GPUs: the custom cursor trails behind the OS pointer, scroll feels heavy, and
section headings flicker/jump when they cross the top of the viewport. Tracing
the built bundle points to main-thread contention from three concurrent
`requestAnimationFrame` loops plus a Framer Motion re-trigger pattern.

Filing here because the Buzz team owns buzz.xyz and can route internally if the
site source lives elsewhere (the page is built with `block/builderbot` per the
runtime config, but the site content doesn't appear to be in any public repo).
Happy to move this if there's a better home.

## Repro

Open https://buzz.xyz/ on integrated graphics (or DevTools CPU 4× throttle):
- Move the mouse while the hero WebGL scene animates → cursor lags ~1–2 frames.
- Scroll → cursor lag gets worse.
- Scroll any section heading past the top of the viewport → it flickers and
jumps down ~30px, then re-animates in.

## 1. WebGL SceneCanvas blocks the main thread

`SceneCanvas-*.js` is **~1 MB** uncompressed and runs its `requestAnimationFrame`
loop + WebGL + pointer tracking **entirely on the main thread**.

| chunk | size |
|---|---|
| `SceneCanvas-*.js` | 1,048,470 B |
| `sites-*.js` | 361,342 B |
| `utils-*.js` | 225,939 B |

Inside the scene chunk: 133 `webgl` refs, 15 `getContext`, 55 `pointer` refs,
7 `requestAnimationFrame`. `OffscreenCanvas` appears 5× but only for
feature-detection / off-screen measurement — no `new Worker`, no
`postMessage`, no `transferControlToOffscreen`. The render loop is on the
main thread and competes with input handlers.

## 2. Three concurrent rAF loops starve the cursor

The custom cursor is a 16px white circle (`mixBlendMode: difference`) tracked
via its own rAF loop. The cursor logic has no easing (direct snap) — it
*should* track 1:1. The lag is because three other rAF loops eat the frame
budget first:

| rAF source | refs in bundle |
|---|---|
| WebGL scene | 7 `requestAnimationFrame` in scene chunk |
| Lenis smooth scroll | 26 `lenis` refs, `virtualScroll`, `isScrolling` |
| Framer Motion | 20 `W.*` motion components |

`mixBlendMode: difference` compounds it — the browser recomposites the full
stacking context (including the WebGL canvas) under the cursor every frame.

## 3. Section headings glitch at the viewport edge

All 7 `whileInView` blocks use `once: false` (zero use `once: true`):

```js
s.jsxs(W.h2, {
initial: { opacity: 0, y: 30 },
whileInView:{ opacity: 1, y: 0 },
viewport: { once: false, amount: 0.45, margin: "-10%" },
transition: { duration: 0.7 },
children: ["A new native workspace",
, "for human + agent teams"]
})
```

`once: false` + `amount: 0.45` → when the heading drops below 45% visible,
Framer Motion resets it to `opacity: 0, y: 30`. Scroll a hair more → it
re-enters → re-animates in over 0.7s. Result: **flicker + vertical jump**
at the viewport edge. The "A new native workspace" heading is the most
visible, but every section does it.

## 4. Cursor should be a bee (feature request)

The cursor is a white circle, not a bee — but the bee SVG already exists in
the bundle (`bee-sprite`, `viewBox: "0 0 466 309"`, with
`bee-wing-left-flap` / `bee-wing-right-flap` keyframe animations). Swapping
the cursor `

` for that SVG would give the bee cursor the brand clearly
wants, and using a colored SVG removes the `mixBlendMode: difference` cost
from §2.

## Suggested fixes

| § | fix | risk |
|---|---|---|
| 1 | Move scene to `OffscreenCanvas` + worker (`transferControlToOffscreen`) | high / biggest win |
| 1 | Pause scene rAF when off-screen / tab hidden | low |
| 2 | Replace JS cursor with `cursor: url(bee.svg), auto` (OS-level, no rAF, no blend mode) | low / big win |
| 3 | Set `once: true` on content headings; use `useScroll`/`useTransform` for scroll-driven motion | low |
| 4 | Render `bee-sprite` SVG as the cursor element | low |

## Stack (confirmed from built assets)

Vite + React + Framer Motion + Lenis + hand-rolled WebGL (no Three.js), custom
CSS (no Tailwind), built via `block/builderbot` (`app.builderlab.xyz`), deployed
as a static SPA.

## Questions

- Where does buzz.xyz site feedback belong? Is the source public somewhere I
missed, or is this routed internally?
- Is there a target device / benchmark the landing page is validated against?
- Open to a PR for the low-risk subset (§2 cursor, §3 `once: true`, §4 bee SVG)
if the source is accessible?

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the landing page on integrated graphics or with DevTools CPU 4× throttling, then inspect the built SceneCanvas-*.js, sites-*.js, and utils-*.js chunks described in the report. Trace the WebGL, Lenis, cursor, and Framer Motion requestAnimationFrame activity and the seven whileInView headings. Done means the reported cursor lag and heading flicker no longer occur under the stated reproduction steps, with the landing page still functioning.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react, vite
Domain
frontend, performance, web-dev
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.