Skip to content

Make the portrait frame set mean something - #376

Merged
singhharsh1708 merged 1 commit into
mainfrom
fix/mobile-variant-means-something
Sep 5, 2026
Merged

Make the portrait frame set mean something#376
singhharsh1708 merged 1 commit into
mainfrom
fix/mobile-variant-means-something

Conversation

@singhharsh1708

Copy link
Copy Markdown
Owner

I had touched this feature twice without ever running it, so I ran both flows in Chrome. The "Generate mobile variant" toggle was generated on the path where its output cannot survive, and ignored on the path where it matters.

Style path: rendered twice, discarded once

what happened two full renders, hasMobileFrames=1 into the editor, 120 portrait frames in IndexedDB
what shipped a 64 KB procedural export, hasMobile=false, mobileCount=0, no frames-mobile/

The second render roughly doubled the wait and was thrown away every time.

The discarding is correct. A style background ships as a recipe and is redrawn at whatever size the viewport is, so a phone already gets a portrait background at its own resolution — strictly better than fixed portrait JPEGs. So the fix is to stop rendering it: the style path now renders once and clears the slot.

Video path: the toggle was never read

handleUpload never looked at generateMobile. No portrait set, no parameter, nothing. Footage is the one case a portrait set cannot be derived from — nothing can redraw someone's video at a new aspect ratio — so that is where the toggle now does its work, as a second sampling pass at 720×1280.

That pass would have shipped distorted frames

ctx.drawImage(video, 0, 0, w, h) scales the whole frame into the box, stretching a 16:9 clip about 3× vertically. I caught this before shipping. It now draws from a centred source rectangle when the ratios differ:

source → target crop aspect deviation
1920×1080 → 720×1280 608×1080 0.08%
3840×2160 → 720×1280 1215×2160 0.00%
640×480 → 720×1280 270×480 0.00%
1080×1920 → 720×1280 no crop 0.00%
1920×1080 → 1280×720 no crop 0.00%

A failed second pass cannot lose the upload that already succeeded — the landscape frames are stored first, and the user is told the phone will fall back to them.

Verified end to end

Real 1280×720 clip uploaded through /create with the toggle on, exported through the real button:

frames/         120 files   1280x720
frames-mobile/  120 files   720x1280      (genuine centre crop, checked by eye)
page:           hasMobile = true   mobileCount = 120

Served and loaded at two widths:

viewport landscape fetched portrait fetched
390×844 0 120
1440×900 120 0

One set, never both — the guard against a phone pulling ~70 MB on cellular still holds, and the canvas paints in both cases.

The toggle's label now names the upload it applies to and says why a style background needs nothing.

Eight new tests in mobileFrames.test.ts, six failing on main (two are pure maths that hold either way). Suite 377 passed.

The "Generate mobile variant" toggle was generated on the path where its output
cannot survive, and ignored on the path where it matters. Both halves measured
by running the flows in Chrome.

Style path, toggle on: two full renders, hasMobileFrames=1 carried into the
editor, 120 portrait frames sitting in IndexedDB, and then a 64 KB procedural
export with hasMobile=false, mobileCount=0 and no frames-mobile folder. The
second render was thrown away every time, having roughly doubled the wait.

That discarding is correct: a style background ships as a recipe and is redrawn
at whatever size the viewport is, so a phone already gets a portrait background
at its own resolution. So the style path now renders once and clears the slot.

Video path, toggle on: handleUpload never read generateMobile. No portrait set,
no parameter, nothing. Footage is the one case a portrait set cannot be derived
from, so that is where the toggle now does its work - a second sampling pass at
720x1280.

That pass would have shipped distorted frames. drawImage(video, 0, 0, w, h)
scales the whole frame into the box, which stretches a 16:9 clip roughly three
times vertically. It now draws from a centred source rectangle when the ratios
differ, keeping the crop's aspect ratio within 0.08% of the target across every
common source size.

Verified from a real 1280x720 clip: 120 landscape frames at 1280x720 and 120
portrait at 720x1280, the portrait one a genuine centre crop. The served page
fetches 120 portrait and 0 landscape at 390x844, and the reverse at 1440x900 -
one set, not both, so the cellular-data guard still holds.

The toggle's label now names the upload it applies to and says why a style
background needs nothing.

Suite 377 passed; six of the new tests fail on main.
@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
scrollcraft Ready Ready Preview Sep 5, 2026 5:31pm UTC

@singhharsh1708
singhharsh1708 merged commit 22bce18 into main Sep 5, 2026
3 checks passed
@singhharsh1708
singhharsh1708 deleted the fix/mobile-variant-means-something branch September 5, 2026 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant