From Idea to v1.2:
The BeatMarker Devlog

This is not a typical changelog. Those are just lists — "added X, fixed Y." This is the story behind those entries: what problem triggered each version, what Claude Code tried that didn't work, and what the solution ended up being. Three versions. Three major obstacles. One video editor who just wanted to stop placing markers by hand.

"Every version started with one frustration. Not a feature request. A frustration."

The Origin: "I Have to Place These Markers by Hand. Again."

Beat-synced editing — cutting video to match the rhythm of music — is one of those workflows that looks effortless in the final cut. Behind the scenes it's tedious: you scrub through the track, identify each beat, place a marker at that timecode, and repeat 200 times for a 4-minute song at 120 BPM. One wrong click, and you're re-placing markers for the next five minutes.

The question was: can this be automated? An AI researcher would say "yes, with a neural network." A video editor with Claude Code said "yes, with a JS library and some math." The neural network version requires a server and a subscription. The JS version runs entirely inside a plugin panel.

The first challenge was just reading an audio file from inside a UXP plugin. The file system API in UXP is asynchronous-only, returns a non-standard proxy object, and requires specific manifest permissions. Claude Code worked through each of those constraints and landed on:

const fs = require('fs');
const raw = await fs.readFile(mediaPath);

// UXP returns a proxy — copy to native ArrayBuffer
const buf = new ArrayBuffer(raw.byteLength ?? raw.length);
new Uint8Array(buf).set(new Uint8Array(raw));

With audio data in hand, the WAV decoder parsed the RIFF header, extracted PCM samples, mixed them to mono, and passed them to music-tempo. The library returned beat timestamps. Those timestamps became colored markers in the Premiere Pro timeline — Red for beat 1, Blue for beats 2 and 4, Yellow for beat 3.

v1.0.0 Initial Release — The first markers land on the beat

What shipped

  • BPM detection from WAV files (any sample rate, PCM 8/16/24-bit + float32)
  • Colored markers: 🔴 beat 1, 🔵 beats 2 & 4, 🟡 beat 3
  • Phase adjustment (◀ ▶) to shift the beat grid without re-analyzing
  • Remove markers — cleans only plugin-created markers via name prefix
  • Bilingual UI (EN/PT-BR) auto-detected from system locale

The problem that triggered v1.1

The plugin worked. Then someone asked: "What if I only want to mark beats 1 and 3? And how do I know if the BPM detection is reliable?" Two valid questions with no answer in v1.0.

"It worked on the first real song tested. That was unexpected. The next 40 minutes were spent trying to break it."

v1.1: "I Trust the Plugin, But Should I?"

Beat detection confidence is not binary. A metronome track: 100% reliable. A jazz improvisation with tempo rubato: maybe 60%. A song with a complex intro that doesn't establish tempo for the first 30 seconds: the detector might find the wrong BPM entirely. Placing 200 wrong markers and not knowing they're wrong is worse than placing 0 markers.

Claude Code needed a way to quantify confidence from the beat timestamps alone — no access to the raw audio model, no probability scores from the library. The solution was the Coefficient of Variation (CV) of beat intervals: if beats are evenly spaced, CV is near 0. If they're erratic, CV is high. A simple formula converts that to a 0–100 score.

💡 The formula CV = stddev(intervals) / mean(intervals) · Confidence = clamp(0, 100, round((1 − CV × 4) × 100))

The confidence indicator needed to appear immediately after analysis — not just as a percentage, but as something communicative. The plugin shows a color-coded bar (green/yellow/red) plus a randomized phrase inspired by Whiplash: "Not quite my tempo" for low confidence, "Rushing? No. Dragging? No. Perfect." for high. Thirty phrases, chosen randomly on each analysis. A small detail that makes the tool feel alive instead of clinical.

Beat selection came from a real workflow need: editors often want only the downbeats (1 and 3) or only the backbeats (2 and 4). Toggling individual beats on/off sounds simple but has a UXP complication — CSS variable-driven colors sometimes don't apply correctly to dynamically updated elements. The fix was inline styles for the beat toggle buttons, bypassing CSS variable resolution entirely.

v1.1.0 Confidence + Beat Selection — Now you know if you can trust it

What shipped

  • BPM Confidence Indicator — color-coded bar with a Whiplash-inspired phrase
  • Beat selection — toggle individual beat positions (1 2 3 4) independently
  • Phase shift with selective beats — colors rotate within active set only
  • UXP CSS fix — inline styles for beat toggles to bypass specificity issues

The problem that triggered v1.2

The plugin only accepted WAV files. Every editor who uses MP3 as their work format was left out. The question was how to decode MP3 inside UXP without crashing the host application.

v1.2: "My Audio Is in MP3. Now What?"

MP3 is everywhere. It's the default export format for most music streaming downloads, the standard for music library sync, and what most music tracks get delivered as. A beat detection plugin that only accepts WAV is a plugin that half the potential users can't use.

The problem: decoding MP3 in JavaScript without WASM. Every high-performance MP3 decoder uses WebAssembly. Every WASM-based decoder Claude Code tested crashed Premiere Pro. Not gracefully — full application crash, no error log. The host process just terminated.

WASM with pthreads is the culprit. UXP's JavaScript environment doesn't support multi-threaded WASM, and many decoders rely on it. Single-threaded WASM variants were also unstable in testing.

The solution: js-mp3. A pure JavaScript implementation of an MP3 decoder — no native code, no WASM, just math. It's slower (2 seconds for a 4-minute track vs. 500ms for WAV), but it's the only option that doesn't crash the host.

MP3 decoding introduced a timing problem that WAV doesn't have. MP3 decoders generate phantom samples at startup — the "encoder delay" recorded in the LAME header, plus a fixed MDCT startup delay specific to js-mp3 (2,070 samples). Both need to be stripped before analysis, or markers land slightly early. After calibrating against a reference decoder, the final offset was exactly 0 frames at any standard video frame rate.

v1.2.0 MP3 Support — The format everyone actually uses

What shipped

  • MP3 support — auto-detected by magic bytes, not file extension
  • Pure JS MP3 decode via js-mp3 (no WASM, no crashes)
  • Encoder delay stripping from Xing/LAME header
  • js-mp3 MDCT startup delay correction (2,070 samples @ 44.1 kHz)
  • Calibrated to 0 frames offset at any standard frame rate

Current status

v1.2 is the current release. The plugins are stable, actively used, and open source. Issues and feature requests welcome on GitHub.

"Three library crashes before finding js-mp3. The fourth one worked."

The Parallel Story: BeatMarker for After Effects

While the Premiere Pro plugin was being built, the same beat detection logic was adapted for After Effects. The difference: After Effects 2026 doesn't support UXP panels for audio, so the AE plugin uses CEP instead — an older platform that runs inside a full Chromium browser with Node.js access.

The AE version shipped as v1.0 with the full feature set from day one, because the Premiere plugin had already solved the hard problems: audio decoding, confidence scoring, beat selection, differential marker updates. The learnings from three Premiere versions went into the AE plugin's v1.0.

That's one of the underrated advantages of building tools for yourself: when you build the second version of something, you already know which problems are hard.


Both plugins are open source and free: BeatMarker for Premiere Pro · BeatMarker for After Effects