Skip to content

Bound Liquid and Frost input queues to prevent frame stalls #42

Description

@NMinhNam

Component

Liquid and Frost

What happened?

Problem

Liquid and Frost can accumulate an unbounded number of pending inputs.

When the component is outside the viewport, the animation loop is paused,
but splat() and melt() can still append to their queues. When the
component becomes visible again, all pending inputs are processed in a
single frame.

This can cause a severe main-thread/GPU stall.

Affected code

  • src/lib/Liquid/LiquidVanilla.ts
  • src/lib/Frost/FrostVanilla.ts

Reproduction

Using a local-only temporary instrumentation:

  1. Hide the Liquid component.
  2. Queue 10,000 splat() calls.
  3. Show the component again.
  4. Record the resume frame in Chrome Performance.

Before the fix:

  • Pending queue: 10,000
  • Observed processing task: approximately 13,000 ms
  • The UI visibly freezes when the component resumes.

This is an imperative API stress case. The normal Liquid pointer interaction
can also generate input continuously, while Frost is affected when consumers
call melt() repeatedly.

Expected behavior

Input queues should remain bounded and should not be drained entirely in one
animation frame.

Proposed solution

  • Ignore new input while the component is outside the viewport.
  • Clear stale pending input when the component becomes hidden.
  • Limit pending inputs.
  • Process only a bounded number of inputs per frame.

Evidence

Attach the Chrome Performance recording and screenshot showing the
approximately 13-second stall.

Steps to reproduce

Reproduction

Using a local-only temporary instrumentation:

  1. Hide the Liquid component.
  2. Queue 10,000 splat() calls.
  3. Show the component again.
  4. Record the resume frame in Chrome Performance.

Before the fix:

  • Pending queue: 10,000
  • Observed processing task: approximately 13,000 ms
  • The UI visibly freezes when the component resumes.

This is an imperative API stress case. The normal Liquid pointer interaction
can also generate input continuously, while Frost is affected when consumers
call melt() repeatedly.

Browser and version

Safari 26.5, Chrome 140

Is the html-in-canvas flag enabled?

No (WebGL overlay fallback)

Framework

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions