Skip to content

The phone downloads 456 KB of uncompressed text every time it opens StemDeck #561

Description

@thcp

The problem

StemDeck has no response compression. Opening the UI on a phone pulls about 456 KB of JavaScript, CSS and HTML in the clear:

file raw would gzip to
static/js/i18n.js 328 KB 86 KB
static/mobile/app.js 46 KB 14 KB
everything else 82 KB 26 KB
total 456 KB 126 KB

Over loopback in the desktop webview this is invisible, which is why it has never been noticed. Over Wi-Fi to a phone it is the load time, and it shows up as the whole UI feeling sluggish, not just the first paint.

Two things make it worse than the raw number suggests:

  • app/main.py sets Cache-Control: no-cache, must-revalidate on every static asset. That is deliberate and correct (stale JS after an update silently breaks the app), but it costs a round trip per file on every load.
  • Browsers generally refuse to keep a disk cache for an origin with a certificate error. On a self-signed https origin the revalidation that would normally answer 304 is likely to fetch the whole payload again, every visit.

Measured, so it is not confused with the other suspect

TLS itself is not the cause. On the same machine:

http https
50 MB bulk 200 MB/s 171 MB/s
880 KB range request 48 ms 55 ms

15%, on a link far faster than any Wi-Fi. Compression is where the win is.

Constraints on any fix

  • A range window of audio must never be compressed. The phone streams stems through static/js/chunkedAudioEngine.js, which asks for five-second windows with a Range header and gets 206 Partial Content back. Compressing one rewrites Content-Length while Content-Range still describes the uncompressed bytes, and browsers do not agree on how to read that. The payload is PCM, so there is nothing to gain either.
  • Event streams must not be buffered. The job and queue progress streams are text/event-stream; a compressor's buffer there is a stall with no error.
  • Whole audio files should be left alone too: WAV does not compress, and the machine paying for it may also be running Demucs.
  • Any dependency added for this would change uv.lock and break the desktop in-app updater, so it needs to come from what is already installed.

Not in scope

i18n.js is 72% of the payload because all eleven language tables ship to every user so that one of them can be read. Splitting it per locale would take 86 KB to roughly 8 KB, but that is a separate and much larger change.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions