Instagram no em deixa avançar un Reel. Així que vaig construir ReelBit.

A Instagram web no pots moure la barra d'un Reel. Ni canviar la velocitat. Ni saltar cinc segons. Si t'has perdut alguna cosa, o vols tornar a veure el gir del final, has de veure el Reel sencer una altra vegada.
Em va molestar prou com per dedicar-li un cap de setmana. Li vaig dir playIg, i la idea era simple: una extensió de Chrome que enganxi uns controls de reproductor de veritat damunt de cada Reel. Avui es diu ReelBit, té landing, política de privacitat i un vídeo promocional, i ja està publicada a la Chrome Web Store. Això és el que va passar entre una cosa i l'altra.
L'enfocament: no barallar-se amb el DOM d'Instagram
És una extensió MV3 sense framework i sense build step. Tres peces:
- un content script + CSS que dibuixa la barra,
- un script minúscul al MAIN world per tocar coses que el content script no veu,
- un popup per als ajustos.
La decisió que més dolor em va estalviar: no posar res dins del DOM d'Instagram. El seu markup canvia, és generat, i qualsevol cosa que hi pengis es trenca al següent deploy. En lloc d'això, la barra és un element amb position: fixed penjat de document.body, i segueix cada <video> llegint el seu getBoundingClientRect. Un únic bucle de requestAnimationFrame compartit i un IntersectionObserver per saber quin vídeo és en pantalla. Res més.
Els gotchas que vaig aprendre a cops
Instagram pausa els vídeos en visibilitychange. Canvies de pestanya i el Reel s'atura. Perquè ReelBit permeti reproducció en segon pla (opt-in), l'script del MAIN world sobreescriu document.hidden. És l'únic lloc on té sentit tocar-ho: el content script viu en un altre món i no ho veuria.
Instagram segresta els clics sobre el Reel. Un clic sobre el vídeo dispara les seves pròpies accions. Els meus botons rebien clics que no eren seus, així que la barra fa stopPropagation abans que arribin al Reel.
El botó de mute és una trampa. El més obvi és video.muted = true. Funciona... i desincronitza l'estat de React d'Instagram: el vídeo calla, però la icona continua dient que sona. La solució és lletja però estable: ReelBit fa clic al propi botó d'àudio d'Instagram. I no el puc buscar per aria-label, perquè està localitzat, ni pel seu SVG, perquè canvia segons l'estat. El trobo per posició: a baix a la dreta, amb un únic SVG de 12px.
Les dates surten del shortcode. Quan la pàgina no porta un <time>, decodifico la data des del shortcode de la URL: és base64 amb un alfabet propi → l'id del media → un timestamp als bits alts.
const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
const EPOCH_MS = 1314220021721n;
function shortcodeToDate(shortcode) {
let id = 0n;
for (const ch of shortcode) id = id * 64n + BigInt(ALPHABET.indexOf(ch));
return new Date(Number((id >> 23n) + EPOCH_MS));
}
(La versió real valida l'alfabet i descarta dates impossibles. Això és la idea.) Amb això, cada Reel mostra la seva data de publicació a la barra i les quadrícules de perfil guanyen una insígnia de data a cada miniatura.
Una barra que no destorba
La primera versió tapava la capçalera de les Stories. Funcional, però emprenyadora. Ara la barra es col·lapsa a una línia fina de progrés i s'expandeix en passar-hi el ratolí per sobre. Si prefereixes tenir-la sempre visible, la fixes amb el pin.

Un historial que Instagram només dona al mòbil
A l'octubre de 2025 Instagram va afegir un historial de Reels vistos, però només a l'app mòbil. A la web, res. ReelBit en guarda un de propi: els últims 500 Reels a chrome.storage.local.
I mai amb cookies. Una cookie a instagram.com viatja als servidors de Meta a cada petició; just el contrari del que vull per a una cosa que és només meva. chrome.storage.local es queda al teu dispositiu, punt.

El nom: de «playIg» a «ReelBit for Instagram»
Abans de publicar vaig llegir les brand guidelines de Meta. No permeten fer servir «Insta», «Gram» ni abreviatures com «IG» al nom d'un producte. L'únic permès, i només de manera descriptiva, és el patró «X for Instagram». Així que playIg va morir i va néixer ReelBit for Instagram. I el disclaimer, ben visible: no està afiliat a Instagram ni a Meta.
Un rename d'última hora fa mal, però és millor que el rebuig a la botiga.
Identitat: pixel art arreu
Ja que el reanomenava, li vaig donar una identitat pròpia. Pixel art:
- el logo és una quadrícula de 16×16,
- les icones són SVG de 9×9 píxels amb
currentColor, així hereten el color del tema, - la font és Silkscreen, que només es veu nítida en múltiples de 8px. Fora d'aquests múltiples, els píxels s'emborrallen.
Les captures de la botiga i el vídeo promocional els renderitzo de manera determinista, frame a frame, amb Playwright + ffmpeg, a partir d'una pàgina HTML que fa servir el CSS i les icones reals de l'extensió. Sense contingut de gent real: els «Reels» de les captures són pixel art de postes de sol i ciutats.

Publicar: la landing i el formulari de privacitat
La landing viu a reelbit.joca.dev, servida per aquest mateix lloc Next.js. No vaig muntar un altre projecte: un check del host al middleware, abans de next-intl, reescriu les rutes del subdomini a l'arbre /[locale]/reelbit. Un lloc, un deploy, dos dominis.
L'altra cosa que no m'esperava: el formulari de privacitat de la Chrome Web Store t'obliga a declarar les dades encara que només visquin al teu dispositiu. ReelBit guarda un historial, així que «historial web» va marcat com a sí, encara que mai surti del navegador. Té sentit, però cal llegir-ho amb calma. Tens la política de privacitat completa.
Què ve ara
Ja està publicada a la Chrome Web Store: Instal·la ReelBit des de la Chrome Web Store.
Mentrestant pots veure la landing o el vídeo de presentació. Si la proves i alguna cosa es trenca, o Instagram canvia alguna cosa i la barra es desquadra, m'interessa saber-ho. És just el tipus de projecte que viu de feedback.