(Notes)

Exporting MP3 in the Browser: Offline Rendering, Encoding Blocks and the Sample Timeline

The web sampler renders a whole cassette offline, encodes mono MP3 at 192 kbps, then outputs a full mix or a ZIP of individual sounds.

Polish Design engineering team・2026-02-20

Contents

After finishing a cassette, a user of the web sampler needs an MP3 that can be taken away, uploaded or dropped into editing software. The arrangement engine computes this file directly in the browser, with no server-side mixing and no recording of live playback. The sections below cover rendering, post-processing, encoding, the two output types and the timeline of sample files. Scheduling and the clock for live playback are covered in Clock design for a web step sequencer, and this article covers output only.

Export uses offline rendering, not a recording of live playback

Recording live playback means export waits for the whole piece to play, so the time needed equals the length of the work. On a phone, audio may also pause when the user switches apps.

Offline rendering does not wait for playback and does not depend on device state at that moment. In the W3C Web Audio API specification, the timeline of an OfflineAudioContext is not tied to the real-time playback clock, and the result is written into an AudioBuffer. MDN's description says it does not output to the device hardware, and instead produces the result as fast as it can into an AudioBuffer. The position of each note is decided by its scheduled time, independent of the browser's state during playback.

Fig. 1A cassette becomes an MP3 in five steps inside the browser, with no live playback involved
  1. Cassette data4 sequences, 12 sound cells
  2. Offline renderingRebuilds the audio graph, scheduled by sequence, cell and step
  3. Whole-mix processingLimiter and a 3 dB reduction overall
  4. Convert to monoGets the Float32 samples
  5. MP3 encoding192 kbps, 1152 samples at a time

The flow follows the path for exporting the full mix.

Rebuilding the audio graph from the same cassette data

The arrangement data is 4 sequences, each with 12 sound cells, and each cell has 32 steps. One step is an eighth note and a bar has 8 steps, so 32 steps make 4 bars. Each sequence sets its own loop length from 1 to 4 bars. Live playback and offline rendering read the same sequence, cell and step data.

Export calls Tone.js's Tone.Offline with a callback function and a total length in seconds. The callback runs in a separate offline environment, so the audio graph has to be rebuilt inside it: each cell that has data gets a player, connected to a PitchShift and a Filter, then to the input of the global processing or to the main channel.

Scheduling uses a single transport.scheduleOnce at time 0:0:0. Inside the callback, three nested loops over sequence, cell and step call player.start directly for every enabled step, and set the volume at the same moment. The start of sequence N is the sum of the lengths of the previous N-1 sequences, so the four sequences line up in order along one continuous timeline.

The total length comes from the loop lengths of the four sequences and the BPM. Each sequence lasts its number of loop bars × 8 × (60 ÷ BPM ÷ 2) seconds. At the default 120 BPM, a step is 0.25 seconds and a bar is 2 seconds, so four sequences of 2 bars each give a total of 16 seconds. The output length depends only on loop length and BPM, not on how many notes are placed.

A person crouching and tightening the bolt on a blue pipe joint with a wrench, with a few lengths of pipe on the floor

Processing before a full export, and mono conversion

For a full export, the output of the global processing chain first passes through a Limiter with a threshold of -1 dB, and the whole mix is then lowered by 3 dB to leave headroom for the sum of many sounds playing together. Once processing is done, the resulting AudioBuffer is converted to mono and handed to the encoder.

MP3 encoding: lamejs, 192 kbps and 1152 samples

Encoding uses lamejs, an MP3 encoder that runs in the browser, created as Mp3Encoder(channels, sample rate, kbps). The channel count and sample rate come straight from the rendered buffer, and the bitrate is set to 192 kbps.

Encoding proceeds in blocks. The MIT Media Lab MPEG Audio FAQ explains that Layer III cuts sound into blocks of 1152 samples, which is a secondary summary, not the original ISO text. The example in the lamejs README also uses 1152, and its comment says any block size works, though a multiple of 576 is easier on the encoder. The arrangement engine feeds the encoder 1152 samples at a time.

Web Audio samples are floating-point numbers between -1 and 1, while the encoder takes 16-bit integers. In conversion, each value is first clamped to between -1 and 1, then negative values are multiplied by 32768 and positive values by 32767, which maps exactly onto the int16 range of -32768 to 32767 with no overflow.

ItemSetting
RenderingTone.Offline, with total length computed from loop length and BPM
ChannelsMono
EncoderMp3Encoder from lamejs
Bitrate192 kbps
Per encoding call1152 samples
Float to integerClamped to -1 to 1, negative times 32768, positive times 32767
Output typeBlob, audio/mp3

Two outputs: a full mix and a ZIP of individual sounds

The export window offers two results. The full mix is a single MP3 of the whole cassette mixed together. The ZIP of individual sounds renders each sound on its own and packs the files into an archive, so each sound can be used separately.

Fig. 2Output splits into three paths by purpose, and public cassettes always upload the full mix
  1. Cassette arrangement is finishedIf nothing is arranged yet, a prompt shows first
  • A complete finished piece is needed
    Single MP3Rendered as a whole, downloaded to the user's device
  • Sounds need adjusting one by one in editing software
    ZIP of individual soundsEach sound rendered once, packed into download.zip
  • Making a cassette public
    Upload the full mixLimit 20 MB, MP3 only

The ZIP of individual sounds works as follows.

  • All 12 sound cells are checked one by one, and a cell is exported only if it has a sample and at least one of the four sequences has a step inside its loop range.
  • Each cell gets its own Tone.Offline run, again in mono and again encoded with lamejs.
  • File names follow the cell number, from sample-0.mp3 to sample-11.mp3, and JSZip packs the files into download.zip for download.

Before a cassette is made public, the system requires a full mix to be exported and uploaded. The file is sent to the backend as a form, and the backend accepts only MP3 with a size limit of 20 MB. On success it returns the file URL, and the frontend records the URL and the export time in the cassette data.

Team members filing cards into the drawers of a large cabinet

The timeline of sample files

MP3 is encoded in frames of fixed length, and most encoders add a delay at the start of the file, with a length that differs between software. If the player does not compensate, a short stretch of silence appears at the start. A single playback shows no difference, but when a sample loops inside a sequence, every cycle begins from the start, and the offset between the start and the beat is always present. When several sounds play together, the beats no longer line up.

The sampler handles this at upload. The backend first converts an uploaded MP3 or WAV into uncompressed AIFF, then converts it to MP3 through the same process. Every sample is produced by one encoding process, so the delay at the start no longer changes with the source software. The frontend does not need to detect the leading silence file by file, or write a separate compensation for each software's differences.

Prompts before export

A full mix must be exported before a cassette is made public. When a cassette has no steps arranged yet, the export window shows the message "No beats yet, so no audio file can be exported" and offers a "How to tap" entry that guides the user to finish arranging first.

When offline rendering does not fit

An OfflineAudioContext needs its length specified at creation, and the whole result is held in memory. For capturing microphone or external instrument input in real time, or for output whose length is not known in advance, a recording approach fits better. A cassette whose length is set by loop and BPM meets the premise of offline rendering.

Further reading and official tutorial videos

For the overall approach to the sampler and how it is used, see the TMC Cloud Sampler case. For beat scheduling in live playback, see Clock design for a web step sequencer.

Demonstrations of export and sampling are in the official tutorial videos: 3-2 Cassette Editing and Export and 2-1 Recording Samples.

For audio rendering, export or sampling features on a web page, the scope is described in Web interactive feature development, and the development team can be reached through describing the web audio requirement.

(Contact)

Start a project.

Three steps to a clear direction and budget range.

  1. 01Book the first meeting: choose a time. A full specification is not required.
  2. 02A 15 to 30 minute online consultation: direction, budget range, and draft timeline.
  3. 03After materials are reviewed, a quote and delivery specification follows in about 3 to 5 working days.
Email
hello@polish-design.com.tw
Mailing address
3F, No. 191-1, Zhifu Rd., Zhongshan Dist., Taipei 104, Taiwan

Book the first meeting

Monday to Friday, 16:00–18:0030 minutes online

Choose a time, then confirm with a Google account or an email address. A confirmed booking can be added to the calendar.

Choose a time

Or send the details first

Not ready to pick a time yet: leave the project details here and the team replies by email. A meeting can still be booked right after sending.

More details (optional): service, stage, links, source

Information is used only to evaluate this project.

ISO/IEC 27001:2022 certificate

(Certificate)

ISO/IEC 27001:2022

Certification body
ARES International Certification
Scope
The development process for interactive immersive 3D systems, and network and infrastructure operations and management.

Verify with ARES