Rename clicktrack.js — a content blocker was eating it and killing the app

A file called `clicktrack.js` matches the click-tracking telemetry patterns that
EasyPrivacy and similar lists block by substring. In a browser with a content
blocker the request never completes, and because it is a module import that
takes the entire graph with it: main.js never runs, no handler is ever bound,
and every control in the UI sits there looking correct and doing nothing.

The failure is unusually expensive to diagnose because everything else looks
healthy. The dev server returns 200 with the right MIME type, curl fetches it
fine, node imports it fine, and the app loads perfectly in any browser without a
blocker — which is how it passed every check here. Only the console names it,
and only as one line about a module that failed to load.

Renamed to metronome.js, which is also the better name for what it does. The
button id went with it, since cosmetic filter rules can hit ids too.

The general rule, recorded at the top of the file: anything shipped to a browser
and named like tracking will be treated as tracking. Avoid click, track,
analytics, pixel, beacon and ad in filenames and URL paths, however honest the
code behind them is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dejvino
2026-08-18 07:01:23 +02:00
co-authored by Claude Opus 5
parent 4267c588aa
commit 4217cca3d3
5 changed files with 93 additions and 9 deletions
+11 -5
View File
@@ -5,7 +5,7 @@ import { formatTime } from './audio/decode.js';
import { describeLook } from './look/LookGenerator.js';
import { toHex } from './look/palette.js';
import { applyGrainToPost, describeGrain, GRAIN_MASKS, GRAIN_MODES } from './look/grain.js';
import { renderClickTrack, audioBufferToWavBlob } from './audio/clicktrack.js';
import { renderClickTrack, audioBufferToWavBlob } from './audio/metronome.js';
import { Exporter, exportSegment, downloadBlob, PRESETS, isSupported } from './export/Exporter.js';
const QUALITY = {
@@ -328,10 +328,10 @@ function renderPanel() {
${s.shots ? `<i class="dim">${s.shots.length} shots</i>` : ''}</span>
<b>${(s.variants || [s.layers]).map((v) => v[0].module.name).join(' / ')}</b>
</div>`).join('')}
<button id="btn-clicktrack" class="wide">download click track</button>
<button id="btn-metronome" class="wide">download click track</button>
<div class="hint">Mixes clicks onto the detected beat grid. If they don't sit on
the beat, tempo detection is wrong and everything downstream inherits it.</div>`;
document.getElementById('btn-clicktrack').addEventListener('click', downloadClickTrack);
document.getElementById('btn-metronome').addEventListener('click', downloadClickTrack);
return;
}
@@ -562,11 +562,11 @@ document.getElementById('btn-segment').addEventListener('click', () => runExport
async function downloadClickTrack() {
if (!state.show.ready) return;
const button = document.getElementById('btn-clicktrack');
const button = document.getElementById('btn-metronome');
button.textContent = 'rendering…';
try {
const buffer = await renderClickTrack(state.show.audioBuffer, state.show.track.tempo);
downloadBlob(audioBufferToWavBlob(buffer), `${state.show.fileName}-clicktrack.wav`);
downloadBlob(audioBufferToWavBlob(buffer), `${state.show.fileName}-metronome.wav`);
button.textContent = 'download click track';
} catch (err) {
button.textContent = `failed: ${err.message}`;
@@ -652,3 +652,9 @@ if (import.meta.env && import.meta.env.DEV) {
requestAnimationFrame(frame);
resize();
// Tell the boot guard in index.html that the module graph made it all the way
// through. Without this the guard cannot distinguish "still starting" from
// "never going to start", and the failure it exists to catch is exactly the one
// that produces no error at all in the page.
window.__FLOW_STATE_READY__ = true;