Files
Miguel Angel Simon Sierra 3ca8f1018e fix(studio): release the previous project's render scopes on switch
Ten project-switch cycles retained 0.24 MB of JS heap per cycle, monotonic
and unbounded. Two causes, both in how memoized callbacks keep render scopes
alive.

A `useCallback` whose deps changed is a NEW closure in the current render's
scope; every callback whose deps did NOT change is the closure from an earlier
render, stored into that same fresh scope. That one edge chains render scope N
to render scope N-1. A project switch changes projectId, activeCompPath and
timelineElements at once, so each switch added a link — and each link pinned
that render's lint result (~240 findings) and its element snapshot plus the
cached index built from it, for the life of the session.

`useLintModal` and `useDomSelection` now build their callbacks once and read
render-scoped values through a ref, so no stale closure lands in a fresh scope.
`groupFindings` moves to module scope and takes the findings as an argument
instead of closing over them.

Separately, the composition-source map was pinning a whole document per open:
V8 represents a substring of 13+ characters as a SlicedString that keeps its
parent alive, so a handful of `compositions/*.html` paths cut out of a 36 KB
`index.html` held that document as long as the map lived.

Measured on two generated fixtures, forced GC between samples, production
React, same method before and after:

  cycles  heap/cycle before  after
  11      0.237 MB           0.100 MB
  21      0.202 MB           0.061 MB

Nodes (+0) and listeners (+0) were already flat once the probe stopped holding
puppeteer ElementHandles, which pin an element and its whole ancestor chain
through a DevTools global handle and read as detached-DOM retention.

The remaining slope is V8 tier-up, not retention: code space grows 81 KB per
open over the first eight opens and 22 KB over the next eight, and the heap
slope decays with it (0.100 -> 0.061) where before it did not (0.237 -> 0.202).
2026-08-03 22:05:35 -07:00
..
2026-08-02 21:57:19 -07:00
…

@hyperframes/studio

Browser-based composition editor UI for Hyperframes. Provides a visual timeline, code editor, and live preview for building video compositions.

Install

npm install @hyperframes/studio

What it does

The studio is a React application with:

  • Visual timeline — drag, resize, and arrange elements on tracks
  • Code editor — edit HTML and GSAP scripts with CodeMirror (syntax highlighting, autocomplete)
  • Live preview — see changes in real time as you edit
  • Composition inspector — view and modify element properties

Development

The studio is embedded in the hyperframes preview command. To develop the studio UI itself:

cd packages/studio
bun run dev        # Start Vite dev server
bun run build      # Build for production
bun run typecheck  # Type-check

Tech stack

  • React 18/19, Zustand (state management)
  • CodeMirror 6 (editor)
  • Tailwind CSS (styling)
  • Vite (bundler)
  • Phosphor Icons

Documentation

Full documentation: hyperframes.heygen.com/packages/studio