Optimisation - throttling for low end devices

GClass runs at full speed by default. No observer throttling and the GSAP ticker follows requestAnimationFrame. On low end devices you can trade a few frames for smoother interaction by capping the ticker and coalescing the three body observers.

js
import { initAnimations, gclassOpts } from 'gclass-anims'

// default: 0 throttle, default ticker (about 60fps via rAF)
initAnimations()

// capped at boot: 1 observer per frame, 30fps ticker
initAnimations(1, 30)
initAnimations({ throttlePerFrame: 1, fps: 30 })

// change on the fly without reload - e.g. low end mode button
gclassOpts(1, 30) // enable: 1 observer per frame, 30fps
gclassOpts()      // reset: 0 throttle, default ticker
gclassOpts({ throttlePerFrame: 2, fps: 45 })

// read current runtime
import { getGClassConfig } from 'gclass-anims'
getGClassConfig() // { throttlePerFrame: 1, fps: 30 }

Demo - what throttling does

Three observers watch document.body for .flip,.appear and .leave. Without throttling they fire immediately in the same microtask. With throttlePerFrame=1 a single hub observer queues mutations and drains them round robin: one observer type per requestAnimationFrame (flip then appear then leave). A mixed burst that touches all three types therefore spreads across three frames.

throttle 0 (default)One long task. All three observers fire together. Fastest completion, highest peak main thread cost. Best for desktop.
throttle 1 + fps 30 (low end)Three short tasks. One observer per frame and ticker capped at 30fps. Lower peak cost, about 32 to 48ms later for a mixed burst. Best for low end devices.
throttle 2 or 3Two or three observers per frame. Middle ground between the two above.

Demo - low end toggle

Copy this pattern for a low end device button. It calls gclassOpts on click and rewires observers immediately without a reload or a boot replay.

html
'use client'
import { gclassOpts } from 'gclass-anims'

export function LowEndToggle() {
  const [on, setOn] = useState(false)
  return (
    <button onClick={() => {
      const next = !on
      setOn(next)
      if (next) gclassOpts(1, 30)
      else gclassOpts() // reset to 0, default ticker
    }}>
      {on ? 'Low-end ON (1, 30)' : 'Enable low-end: gclassOpts(1, 30)'}
    </button>
  )
}

Tunables

throttlePerFrameObservers per rAF frame. 0 = no throttling (default, 3 observers immediate). 1 = round robin one per frame. 2 to 3 = two or three per frame
fpsGSAP ticker cap. 0 = default rAF (about 60fps, no cap). 30 = low end, 45 = balanced, 60 = capped 60
initAnimations(throttle, fps)Set at boot. Also accepts initAnimations({throttlePerFrame, fps}). No args keeps last gclassOpts values
gclassOpts(throttle, fps)Live switch. Both args optional, missing falls back to 0. gclassOpts() resets. Also accepts gclassOpts({throttlePerFrame, fps}). Rewires observers if running
getGClassConfig()Read current {throttlePerFrame, fps}
subscribeGClassConfig(cb)Subscribe to config changes, returns unsubscribe

Defaults are intentionally minimal. Keep 0 and default ticker for marketing pages and desktops. Enable gclassOpts(1, 30) only when you detect a low end device or when a stress test with 150 to 200 simultaneous .appear inserts shows a long task over 50ms in the Performance panel. The hub never drops mutations, it only spreads them. A mixed burst with all three types still completes, it just takes up to three frames.