Skip to content

Lazy-load player adapters on demand via ESM dynamic import #229

Description

@maboa

Problem

Once player adapters are split into separate files (#228), consumers using non-native players still have to include the right <script> tag(s) manually. The library already knows which adapter is needed — it reads data-player-type on the player element at instantiation. Letting the core fetch the adapter itself would remove the manual step entirely.

Proposed direction

Once the library is distributed as ES modules (#218), the hyperaudioPlayer factory can do something like:

async function hyperaudioPlayer(playerType, instance) {
  if (playerType === 'native') return new NativePlayer(instance);
  const mod = await import(`./players/${playerType}.js`);
  return new mod.default(instance);
}

That means:

  • Consumers using SoundCloud/YouTube/Vimeo/VideoJS/Spotify can drop the extra <script> tag from their HTML. Including the core is enough.
  • Native-only consumers (the common case) never pay for adapter bytes.
  • Paths are module-relative — no document.currentScript.src discovery, no global registration shims, no consumer-configured playerPath option.

Why this needs #218 first

Without ESM, doing the same thing with classic scripts requires:

  • A custom loader using dynamic <script> injection
  • Base-path discovery via document.currentScript.src (fragile)
  • A global-registration namespace for adapters to attach themselves to
  • Manual error handling for failed loads

All of that gets replaced by a one-line import() once ESM is in place. Better to wait than build a loader we throw away.

Why this needs #228 first

The adapters need to be in separate files before they can be loaded separately. #228 does that refactor without changing runtime behaviour.

Compat notes

Once shipped, this changes behaviour in a few subtle ways consumers should know about:

  • Constructor becomes async-ish. new HyperaudioLite(...) returns before the adapter has loaded. instance.myPlayer won't exist for a brief window. Transcript clicks during that window need to queue (or no-op gracefully). Direct access patterns like instance.myPlayer.play() immediately after construction break.
  • First-play latency. On slow connections, clicking play within the first ~200ms after page load might wait for the adapter fetch. Usually imperceptible.
  • Error UX shifts to runtime. A 404 on the adapter file shows up at HAL instantiation rather than at page load. Needs a clear console error.
  • YouTube API script load serialises. Currently the YouTube IFrame API script load happens during page load. With lazy loading, it's: youtube.js fetch → then YT API script fetch → then ready. Two round trips instead of one.

#217 (options-object constructor) would give a natural place to expose an onReady callback for consumers who need to know when the adapter has finished loading.

Related

  • Depends on #218 — ESM/CJS distribution
  • Depends on #228 — split adapters into separate files
  • Touches #217 — options-object constructor (natural home for an onReady callback)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions