Skip to content

Legacy sorter worker (~50% of the ESM payload) ships and materializes even when unused #409

Description

@morgan-env

Summary

Upgrading from 0.1.10 to 2.1.0, our bundled Spark chunk grew from ~575 KB raw / 156 KB gzip
to ~5.02 MB raw / 1.78 MB gzip. Auditing the built chunk reveals that ~47% of it is the legacy sorter
worker (oldWorker-*), which the current (non-Old*) API never executes
:

  • oldWorker-* inline string literal: 2,348,093 chars ≈ 46.8% of the chunk
    (~816 KB of the chunk's 1.78 MB gzip on its own)
  • current worker-* literal: 2,116,988 chars ≈ 42.2%
  • together the two inlined workers are ~89% of the shipped bytes

Why bundlers can't remove it

Both workers are inlined as top-level string constants, and both are wrapped in
new Blob([...]) constructed at module scope, so they materialize on chunk evaluation
regardless of which code path is used. The package also declares no "sideEffects" field,
so no bundler (we measured with Next.js/Turbopack production builds, but this is
bundler-agnostic) can tree-shake the legacy path even when only the new
SparkRenderer/SplatMesh API is imported.

Ask

Any of these would roughly halve the payload for apps on the current API:

  1. Move the Old*/legacy path (and its worker string) behind a subpath export
    (e.g. @sparkjsdev/spark/legacy), or
  2. Construct the worker Blobs lazily on first use instead of at module scope, and add
    "sideEffects": false (or an accurate list) to package.json so the unused branch is
    shakeable, or
  3. Ship a secondary "modern-only" build.

Happy to provide the full chunk-analysis methodology/numbers if useful.
Thanks — the 2.x
renderer itself has been a clean upgrade for us otherwise :)

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