This is the second post in a series about auto-scroll capture in a Chrome extension. The first post was the problem. This one is the fork I had to make: film the live tab, or render motion from a still image of the page.
They look similar in the UI. They are not the same product.
The same intent, two physics
I wanted a recording that moved down a site without my hand on the wheel. That sentence hides a choice.
Either the file is a recording of what the tab painted, in real time, while something else drives scroll.
Or the file is a reconstruction: capture the page once as a tall bitmap, then move a virtual camera across that bitmap on a clock I control.
Both are local. Nothing is sent to a render farm. They fail in opposite ways.
Live capture: the tab is the source of truth
Live capture uses Chrome’s tab recording path. Auto-scroll runs in the page. The encoder writes whatever the compositor put on screen.
If a video is playing, it is in the file. If a menu opens on hover, it is in the file. If a WebGL scene reacts to wheel input, that reaction is in the file. I am not inventing those pixels. I am sampling them.
That honesty has a cost. The encoder is bound to wall-clock time. It cannot pause the universe to finish a heavy frame. When the software video encoder is overloaded, Chrome does not slow the page down. It drops frames. The screen can still look smooth while the file becomes a slideshow.
Tab capture also has geometry quirks. It does not automatically mean “the quality label the user picked.” It films the tab in screen pixels, then fits that into a constraint box. A box with the wrong aspect ratio letterboxes the content: black bars that you will stitch into a map if you are not careful. I size the box from the tab’s real aspect and I never ask capture to upscale. Extra pixels are interpolation, paid for by the encoder.
The live scroller and the recorder are only loosely coupled. Prepare the page, start recording, then start scroll - except when I need the load animation, in which case the recorder must start before the reload. There is no shared frame index. Time in the file is “when the compositor happened to emit a frame,” not “tick N of my scroll loop.”
Cinema: the map is the source of truth
Cinema mode does the opposite. It builds a full-page still — the same family of problem as a stitched PNG — then treats that image as a set. A virtual camera has a position, a speed, an easing curve, and a shutter.
Because the page is a bitmap, I can sample several camera positions inside one output frame and average them. That is real motion blur: the integral of the shutter, not a directional smear guessed from velocity. Timing is frame index / fps. If I ask for a given cadence, I get that cadence. Dropped live frames are not part of this model.
The map’s aspect has to be the output’s aspect before capture. Format first, map second. If I resize the window after the map exists, zoom-1 no longer frames the page. The timeline of scroll positions can survive a recapture. The pixels cannot.
What the map cannot contain, the render cannot show. Hover that was never open. A <video> that was paused or never in view. WebGL that is a live scene, not a screenshot. CSS that animates on a timer rather than on scroll position. Cinema is precise about framing and rhythm. It is silent about anything that needs the page to keep running.
GIF export stays on the live path. Building a palette from a compositor-style render is a different, heavier job. I did not pretend Cinema could own every container.
How I choose
I treat this as a question about what must be true in the file.
If the demo is “this landing page has a looping hero and a scroll-triggered scene,” live is the only honest tool. Cinema would freeze the hero.
If the demo is “move down this article with a stable speed and a readable blur,” Cinema is the tool. Live will give me variable frame timing and whatever the encoder survived.
A stitched PNG is the third deliverable: no motion at all. It shares the map pipeline’s sticky-header problem, which is the next post. It does not share Cinema’s camera.
I do not blend the two in one file. A hybrid that “mostly” uses a map and “sometimes” samples the live tab would lie about both clocks.
What this does not solve
Live still cannot see into a cross-origin iframe. Cinema cannot either: the map is a screenshot of the outer page.
Live still goes black if the tab is hidden. Cinema’s map capture has the same constraint while the still is being taken, then the render is offline.
A page with no real scrollbar does not give Cinema a meaningful map height, and it does not give live auto-stop. That is a driving problem, not a compositing problem.
The useful discipline was naming the fork in the product, not hiding it. “Record the tab” and “render a camera over a poster” are both capture. They are not interchangeable.
If you ship site demos, the question is which lie you refuse: lost interactivity, or lost timing.
ScrollFlow keeps both engines on auto-scroll for that reason. I would rather the user pick an honest mode than get a file that looks fine and explains nothing.
Top comments (0)