{"id":20054,"date":"2026-09-21T22:29:11","date_gmt":"2026-09-21T20:29:11","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/toolbox\/gapless-playback-encoder-delay-checker\/"},"modified":"2026-09-21T22:30:53","modified_gmt":"2026-09-21T20:30:53","slug":"gapless-playback-encoder-delay-checker","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/","title":{"rendered":"Gapless Playback Checker: encoder delay and padding in MP3, AAC, Opus and FLAC"},"content":{"rendered":"<div class=\"lw-gl\" id=\"gl-wurzel\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<style>\n.lw-gl .gl-zone { border: 2px dashed var(--border, #2a2a35); border-radius: 8px; padding: 22px; text-align: center; }\n.lw-gl .gl-zone.gl-ueber { border-color: var(--accent, #2ba6df); }\n.lw-gl .gl-kacheln { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin: 16px 0 4px; }\n.lw-gl .gl-kachel { background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 8px; padding: 11px 14px; min-width: 0; }\n.lw-gl .gl-kachel-name { font-size: 0.74rem; color: var(--text-secondary, #a0a0b0); text-transform: uppercase; letter-spacing: 0.4px; }\n.lw-gl .gl-kachel-wert { font-family: monospace; font-size: 1.4rem; font-weight: 700; color: var(--text-primary, #e8e8ee); line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.lw-gl .gl-urteil { border-radius: 8px; padding: 12px 15px; margin-top: 14px; border: 1px solid var(--border, #2a2a35); background: var(--bg-body, #14141a); }\n.lw-gl .gl-urteil-kopf { font-weight: 700; color: var(--text-primary, #e8e8ee); margin-bottom: 4px; }\n.lw-gl .gl-ja .gl-urteil-kopf { color: var(--ok, #7ee787); }\n.lw-gl .gl-teils .gl-urteil-kopf { color: var(--gl-warn); }\n.lw-gl .gl-nein .gl-urteil-kopf { color: var(--fehler, #f0857a); }\n.lw-gl { --gl-warn: #e8a33d; }\n:root[data-theme=\"light\"] .lw-gl { --gl-warn: #8f5300; }\n.lw-gl .gl-tabelle { overflow-x: auto; margin-top: 6px; }\n.lw-gl table { border-collapse: collapse; width: 100%; font-size: 0.86rem; }\n.lw-gl th, .lw-gl td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border, #2a2a35); }\n.lw-gl th { color: var(--text-primary, #e8e8ee); white-space: nowrap; }\n.lw-gl td { color: var(--text-primary, #e8e8ee); }\n.lw-gl td.gl-wert { font-family: monospace; color: var(--text-secondary, #a0a0b0); }\n.lw-gl .gl-h { font-weight: 700; color: var(--text-primary, #e8e8ee); font-size: 0.95rem; margin: 24px 0 8px; }\n.lw-gl .gl-klein { font-size: 0.84rem; color: var(--text-secondary, #a0a0b0); line-height: 1.55; }\n.lw-gl .gl-knopf { background: transparent; color: var(--text-secondary, #a0a0b0); border: 1px solid var(--border, #2a2a35); padding: 9px 14px; border-radius: 6px; cursor: pointer; font-size: 0.9rem; margin-top: 14px; }\n.lw-gl .gl-fehler { color: var(--fehler, #f0857a); font-size: 0.88rem; margin-top: 12px; }\n.lw-gl a { color: var(--accent, #2ba6df); }\n@media (max-width: 480px) {\n    .lw-gl .gl-kacheln { grid-template-columns: 1fr 1fr; gap: 8px; }\n    .lw-gl .gl-kachel-wert { font-size: 1.15rem; }\n    .lw-gl th, .lw-gl td { padding: 6px 6px; }\n}\n<\/style>\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 18px;\">Lossy encoders add samples at the start and the end of a file. Where the file declares how many, a player can remove them again and one track follows the next without a gap. This check reads those declarations out of the file. Nothing is uploaded, the file stays in the browser.<\/p>\n<div id=\"gl-zone\" class=\"gl-zone\">\n<p style=\"margin: 0 0 12px; color: var(--text-primary, #e8e8ee); font-weight: 700;\">Choose an audio file<\/p>\n<p>        <button id=\"gl-knopf\" type=\"button\" class=\"button\" style=\"background: var(--accent, #7ee787); color: var(--on-accent, #0b1114); border: none; padding: 11px 22px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Select file<\/button><br \/>\n        <input id=\"gl-datei\" type=\"file\" accept=\"audio\/*,.mp3,.m4a,.mp4,.aac,.opus,.ogg,.oga,.flac,.wav,.wave\" hidden><\/p>\n<p class=\"gl-klein\" style=\"margin: 12px 0 0;\">MP3, M4A\/AAC, Opus, Ogg Vorbis, FLAC and WAV. Only the header is read, not the audio.<\/p>\n<\/p><\/div>\n<div id=\"gl-fehler\" class=\"gl-fehler\" data-mp-mask hidden><\/div>\n<div id=\"gl-ausgabe\" data-mp-mask style=\"margin-top: 18px;\" aria-live=\"polite\" hidden>\n<div id=\"gl-kacheln\" class=\"gl-kacheln\"><\/div>\n<div id=\"gl-urteil\" class=\"gl-urteil\"><\/div>\n<div class=\"gl-h\">What the file declares<\/div>\n<div class=\"gl-tabelle\">\n<table>\n<thead>\n<tr>\n<th>Field<\/th>\n<th>Value<\/th>\n<\/tr>\n<\/thead>\n<tbody id=\"gl-felder\"><\/tbody>\n<\/table><\/div>\n<p>        <button id=\"gl-kopieren\" type=\"button\" class=\"gl-knopf\">Copy result<\/button><br \/>\n        <span id=\"gl-kopiert\" class=\"gl-klein\" style=\"margin-left: 10px;\"><\/span>\n    <\/div>\n<div class=\"gl-h\">What the fields mean<\/div>\n<p class=\"gl-klein\" style=\"margin: 0 0 6px;\">Encoders based on a transform need a run-up: the first block of output describes audio that starts before the file does, and the last block reaches past its end. Both ends are therefore filled with samples that are not music.<\/p>\n<p class=\"gl-klein\" style=\"margin: 0 0 6px;\">MP3 carries the numbers in the Info or Xing frame written by the encoder, together with the encoder name. On top of the delay stated there comes the delay of the decoder itself; this check adds 529 samples for it, which is what the measurement on the accompanying article showed.<\/p>\n<p class=\"gl-klein\" style=\"margin: 0 0 6px;\">In MP4 the start is described by the edit list, which moves the start of the track. Apple&#039;s iTunSMPB field states both ends in samples; where it is missing, only the track duration tells a player where to stop.<\/p>\n<p class=\"gl-klein\" style=\"margin: 0 0 6px;\">Opus states its pre-skip in the header, counted at 48 kHz, and the last page says how far the audio really reaches. RFC 7845 defines both, so gapless playback needs no extra field.<\/p>\n<p class=\"gl-klein\" style=\"margin: 0 0 6px;\">FLAC and WAV store the exact number of samples, so nothing is added and nothing has to be removed.<\/p>\n<p class=\"gl-klein\" style=\"margin: 20px 0 0;\">Sources:<br \/>\n        <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7845\" rel=\"noopener\">RFC 7845<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc9639\" rel=\"noopener\">RFC 9639<\/a> \u00b7<br \/>\n        <a href=\"https:\/\/lame.sourceforge.io\/tech-FAQ.txt\" rel=\"noopener\">LAME tech FAQ<\/a>\n    <\/p>\n<p class=\"gl-klein\" style=\"font-size: 0.8rem; margin: 14px 0 0;\">Only the headers are read, not the audio. What a particular player does with the fields is not checked here; the values show what it could do.<\/p>\n<\/div>\n<p><script>\n(function () {\n    'use strict';\n    const T = {\"dezimal\":\".\",\"einleitung\":\"Lossy encoders add samples at the start and the end of a file. Where the file declares how many, a player can remove them again and one track follows the next without a gap. This check reads those declarations out of the file. Nothing is uploaded, the file stays in the browser.\",\"zone_titel\":\"Choose an audio file\",\"knopf_datei\":\"Select file\",\"zone_hinweis\":\"MP3, M4A\\\/AAC, Opus, Ogg Vorbis, FLAC and WAV. Only the header is read, not the audio.\",\"k_format\":\"Format\",\"k_vorlauf\":\"Delay at the start\",\"k_nachlauf\":\"Padding at the end\",\"k_luecke\":\"Gap without the fields\",\"h_angaben\":\"What the file declares\",\"sp_feld\":\"Field\",\"sp_wert\":\"Value\",\"f_format\":\"Format\",\"f_rate\":\"Sample rate\",\"f_kanaele\":\"Channels\",\"f_rahmen\":\"Info frame\",\"f_encoder\":\"Encoder\",\"f_vorlauf_roh\":\"Delay in samples\",\"f_nachlauf_roh\":\"Padding in samples\",\"f_quellrate\":\"Original sample rate\",\"f_granule\":\"Last granule position\",\"f_elst\":\"Edit list\",\"f_itunsmpb\":\"iTunSMPB\",\"f_tiefe\":\"Bit depth\",\"f_abtastwerte\":\"Samples of audio\",\"f_dauer\":\"Duration\",\"format_mp3\":\"MP3\",\"format_mp4\":\"MP4 \\\/ AAC\",\"format_opus\":\"Opus in Ogg\",\"format_vorbis\":\"Ogg Vorbis\",\"format_flac\":\"FLAC\",\"format_wav\":\"WAV\",\"elst_wert\":\"start {start}, duration {dauer}\",\"itunsmpb_wert\":\"{vorlauf} before, {nachlauf} after\",\"wert_keiner\":\"none\",\"wert_unbekannt\":\"unknown\",\"urteil_ja\":\"Gapless by the format itself\",\"urteil_mit_angaben\":\"Gapless only where the fields are honoured\",\"urteil_teils\":\"Start declared, end not\",\"urteil_nein\":\"No declaration in the file\",\"urteil_text_ja\":\"The number of audio samples follows from the format, so a decoder ends the track exactly where it ends. Nothing has to be trimmed by hand.\",\"urteil_text_mit_angaben\":\"The file says how many samples belong to the encoder rather than to the music. A player that reads those fields plays one track into the next; a player that ignores them inserts the gap shown above.\",\"urteil_text_teils\":\"The start is described by the edit list, the end is not described separately. A player that also honours the track duration still ends up at the right length; one that only honours the start keeps the padding as silence.\",\"urteil_text_nein\":\"The file carries no field for delay or padding. Every player therefore keeps the samples the encoder added, and a gap of at least 25 ms remains between two tracks.\",\"bericht_kopf\":\"Gapless check\",\"knopf_kopieren\":\"Copy result\",\"kopiert\":\"copied\",\"kopieren_fehler\":\"copying not possible\",\"fehler_format\":\"This file is not one of the formats read here: MP3, M4A\\\/AAC, Opus, Ogg Vorbis, FLAC or WAV.\",\"fehler_lesen\":\"The file could not be read.\",\"h_erklaerung\":\"What the fields mean\",\"erkl_grund\":\"Encoders based on a transform need a run-up: the first block of output describes audio that starts before the file does, and the last block reaches past its end. Both ends are therefore filled with samples that are not music.\",\"erkl_mp3\":\"MP3 carries the numbers in the Info or Xing frame written by the encoder, together with the encoder name. On top of the delay stated there comes the delay of the decoder itself; this check adds 529 samples for it, which is what the measurement on the accompanying article showed.\",\"erkl_aac\":\"In MP4 the start is described by the edit list, which moves the start of the track. Apple\\u0027s iTunSMPB field states both ends in samples; where it is missing, only the track duration tells a player where to stop.\",\"erkl_opus\":\"Opus states its pre-skip in the header, counted at 48 kHz, and the last page says how far the audio really reaches. RFC 7845 defines both, so gapless playback needs no extra field.\",\"erkl_flac\":\"FLAC and WAV store the exact number of samples, so nothing is added and nothing has to be removed.\",\"quellen_titel\":\"Sources\",\"grenze\":\"Only the headers are read, not the audio. What a particular player does with the fields is not checked here; the values show what it could do.\"};<\/p>\n<p>    function el(id) { return document.getElementById(id); }\n    function text(schluessel, werte) {\n        var s = T[schluessel] || '';\n        return s.replace(\/\\{(\\w+)\\}\/g, function (_, k) { return werte && werte[k] !== undefined ? werte[k] : ''; });\n    }\n    function zahl(wert, stellen) {\n        if (wert === null || wert === undefined || !isFinite(wert)) { return '\u2013'; }\n        var s = Number(wert).toFixed(stellen === undefined ? 1 : stellen);\n        return T.dezimal === ',' ? s.replace('.', ',') : s;\n    }<\/p>\n<p>    \/\/ ---- kleine Leser ------------------------------------------------------\n    function ascii(dv, pos, laenge) {\n        var s = '';\n        for (var i = 0; i < laenge; i++) {\n            if (pos + i >= dv.byteLength) { break; }\n            s += String.fromCharCode(dv.getUint8(pos + i));\n        }\n        return s;\n    }\n    function suchen(dv, muster, von, bis) {\n        var ende = Math.min(bis === undefined ? dv.byteLength : bis, dv.byteLength) - muster.length;\n        for (var i = Math.max(0, von || 0); i <= ende; i++) {\n            var treffer = true;\n            for (var j = 0; j < muster.length; j++) {\n                if (dv.getUint8(i + j) !== muster.charCodeAt(j)) { treffer = false; break; }\n            }\n            if (treffer) { return i; }\n        }\n        return -1;\n    }\n\n    \/\/ ---- MP3 ---------------------------------------------------------------\n    var MP3_RATEN = [[11025, 12000, 8000], [0, 0, 0], [22050, 24000, 16000], [44100, 48000, 32000]];\n\n    function id3Laenge(dv) {\n        if (dv.byteLength < 10 || ascii(dv, 0, 3) !== 'ID3') { return 0; }\n        var g = 0;\n        for (var i = 6; i < 10; i++) { g = (g << 7) | (dv.getUint8(i) &#038; 0x7F); }\n        return g + 10;\n    }\n\n    function mp3Rahmen(dv, von) {\n        for (var i = von; i < Math.min(dv.byteLength - 4, von + 200000); i++) {\n            if (dv.getUint8(i) !== 0xFF || (dv.getUint8(i + 1) &#038; 0xE0) !== 0xE0) { continue; }\n            var b1 = dv.getUint8(i + 1), b2 = dv.getUint8(i + 2), b3 = dv.getUint8(i + 3);\n            var version = (b1 >> 3) & 3, lage = (b1 >> 1) & 3, rateZeiger = (b2 >> 2) & 3;\n            if (version === 1 || lage === 0 || rateZeiger === 3) { continue; }\n            var rate = MP3_RATEN[version][rateZeiger];\n            if (!rate) { continue; }\n            return { pos: i, rate: rate, version: version, lage: lage, kanaele: ((b3 >> 6) & 3) === 3 ? 1 : 2 };\n        }\n        return null;\n    }<\/p>\n<p>    function mp3Lesen(puffer) {\n        var dv = new DataView(puffer);\n        var start = id3Laenge(dv);\n        var rahmen = mp3Rahmen(dv, start);\n        if (!rahmen) { return null; }\n        var i = -1, art = '';\n        for (var k = 0; k < 2; k++) {\n            var m = k === 0 ? 'Info' : 'Xing';\n            var p = suchen(dv, m, rahmen.pos, rahmen.pos + 1200);\n            if (p >= 0 && (i < 0 || p < i)) { i = p; art = m; }\n        }\n        var ergebnis = { typ: 'mp3', rate: rahmen.rate, kanaele: rahmen.kanaele, rahmen: null,\n                         encoder: null, vorlauf: null, nachlauf: null, abtastwerte: null };\n        if (i < 0) { return ergebnis; }\n        var flaggen = dv.getUint32(i + 4), p2 = i + 8;\n        if (flaggen &#038; 1) { ergebnis.rahmenzahl = dv.getUint32(p2); p2 += 4; }\n        if (flaggen &#038; 2) { p2 += 4; }\n        if (flaggen &#038; 4) { p2 += 100; }\n        if (flaggen &#038; 8) { p2 += 4; }\n        ergebnis.rahmen = art;\n        if (p2 + 24 > dv.byteLength) { return ergebnis; }\n        ergebnis.encoder = ascii(dv, p2, 9).replace(\/[^\\x20-\\x7E]\/g, '');\n        var a = dv.getUint8(p2 + 21), b = dv.getUint8(p2 + 22), c = dv.getUint8(p2 + 23);\n        ergebnis.vorlauf = (a << 4) | (b >> 4);\n        ergebnis.nachlauf = ((b & 0x0F) << 8) | c;\n        if (ergebnis.rahmenzahl) {\n            var jeRahmen = rahmen.version === 3 ? 1152 : 576;\n            ergebnis.abtastwerte = ergebnis.rahmenzahl * jeRahmen - ergebnis.vorlauf - ergebnis.nachlauf;\n        }\n        return ergebnis;\n    }\n\n    \/\/ ---- MP4 \/ M4A ---------------------------------------------------------\n    function mp4Kaesten(dv, von, bis, tiefe, sammeln) {\n        var p = von;\n        while (p + 8 <= bis) {\n            var groesse = dv.getUint32(p), name = ascii(dv, p + 4, 4), inhalt = p + 8;\n            if (groesse === 1) { groesse = dv.getUint32(p + 8) * 4294967296 + dv.getUint32(p + 12); inhalt = p + 16; }\n            if (groesse === 0) { groesse = bis - p; }\n            if (groesse < 8 || p + groesse > bis) { break; }\n            sammeln(name, inhalt, p + groesse);\n            if (tiefe > 0 && \/^(moov|trak|mdia|minf|stbl|edts|udta|meta|ilst|----)$\/.test(name)) {\n                var innen = name === 'meta' ? inhalt + 4 : inhalt;\n                mp4Kaesten(dv, innen, p + groesse, tiefe - 1, sammeln);\n            }\n            p += groesse;\n        }\n    }<\/p>\n<p>    function mp4Lesen(puffer) {\n        var dv = new DataView(puffer);\n        var e = { typ: 'mp4', rate: null, kanaele: null, elst: null, itunsmpb: null, dauer: null, zeitbasis: null };\n        var mdhdRate = null, letzterName = null;\n        mp4Kaesten(dv, 0, dv.byteLength, 6, function (name, von, bis) {\n            if (name === 'mvhd') {\n                var v = dv.getUint8(von);\n                e.zeitbasis = v === 1 ? dv.getUint32(von + 20) : dv.getUint32(von + 12);\n            } else if (name === 'mdhd') {\n                var v2 = dv.getUint8(von);\n                mdhdRate = v2 === 1 ? dv.getUint32(von + 20) : dv.getUint32(von + 12);\n                e.rate = e.rate || mdhdRate;\n                e.abtastwerte = v2 === 1 ? dv.getUint32(von + 28) : dv.getUint32(von + 16);\n            } else if (name === 'elst') {\n                var version = dv.getUint8(von), anzahl = dv.getUint32(von + 4);\n                if (anzahl > 0) {\n                    if (version === 1) {\n                        e.elst = { anzahl: anzahl, dauer: dv.getUint32(von + 12) * 4294967296 + dv.getUint32(von + 16),\n                                   start: dv.getInt32(von + 24) };\n                    } else {\n                        e.elst = { anzahl: anzahl, dauer: dv.getUint32(von + 8), start: dv.getInt32(von + 12) };\n                    }\n                }\n            } else if (name === 'mp4a') {\n                e.kanaele = dv.getUint16(von + 16);\n                e.rate = dv.getUint16(von + 24);\n            } else if (name === 'name') {\n                letzterName = ascii(dv, von + 4, bis - von - 4);\n            } else if (name === 'data' && letzterName && \/iTunSMPB\/.test(letzterName)) {\n                var roh = ascii(dv, von + 8, Math.min(120, bis - von - 8));\n                var zahlen = roh.match(\/[0-9A-Fa-f]{8,16}\/g) || [];\n                if (zahlen.length >= 4) {\n                    e.itunsmpb = { vorlauf: parseInt(zahlen[1], 16), nachlauf: parseInt(zahlen[2], 16),\n                                   abtastwerte: parseInt(zahlen[3], 16) };\n                }\n                letzterName = null;\n            }\n        });\n        return e;\n    }<\/p>\n<p>    \/\/ ---- Ogg: Opus und Vorbis ----------------------------------------------\n    function letzteGranule(dv) {\n        for (var i = dv.byteLength - 14; i >= 0 && i > dv.byteLength - 70000; i--) {\n            if (ascii(dv, i, 4) === 'OggS') {\n                var lo = dv.getUint32(i + 6, true), hi = dv.getUint32(i + 10, true);\n                return hi * 4294967296 + lo;\n            }\n        }\n        return null;\n    }<\/p>\n<p>    function oggLesen(puffer) {\n        var dv = new DataView(puffer);\n        var o = suchen(dv, 'OpusHead', 0, 65536);\n        if (o >= 0) {\n            return { typ: 'opus', kanaele: dv.getUint8(o + 9), vorlauf: dv.getUint16(o + 10, true),\n                     quellrate: dv.getUint32(o + 12, true), rate: 48000, granule: letzteGranule(dv) };\n        }\n        var v = suchen(dv, '\\x01vorbis', 0, 65536);\n        if (v >= 0) {\n            return { typ: 'vorbis', kanaele: dv.getUint8(v + 11), rate: dv.getUint32(v + 12, true),\n                     granule: letzteGranule(dv) };\n        }\n        return null;\n    }<\/p>\n<p>    \/\/ ---- FLAC und WAV ------------------------------------------------------\n    function flacLesen(puffer) {\n        var dv = new DataView(puffer);\n        if (ascii(dv, 0, 4) !== 'fLaC' || dv.byteLength < 42) { return null; }\n        var p = 8;                                    \/\/ 4 Kennung + 4 Blockkopf\n        var rate = (dv.getUint8(p + 10) << 12) | (dv.getUint8(p + 11) << 4) | (dv.getUint8(p + 12) >> 4);\n        var kanaele = ((dv.getUint8(p + 12) >> 1) & 7) + 1;\n        var tiefe = (((dv.getUint8(p + 12) & 1) << 4) | (dv.getUint8(p + 13) >> 4)) + 1;\n        var hoch = dv.getUint8(p + 13) & 0x0F;\n        var abtastwerte = hoch * 4294967296 + dv.getUint32(p + 14);\n        return { typ: 'flac', rate: rate, kanaele: kanaele, tiefe: tiefe, abtastwerte: abtastwerte };\n    }<\/p>\n<p>    function wavLesen(puffer) {\n        var dv = new DataView(puffer);\n        if (ascii(dv, 0, 4) !== 'RIFF' || ascii(dv, 8, 4) !== 'WAVE') { return null; }\n        var p = 12, rate = null, kanaele = null, tiefe = null, datenlaenge = null, block = null;\n        while (p + 8 <= dv.byteLength) {\n            var kennung = ascii(dv, p, 4), laenge = dv.getUint32(p + 4, true);\n            if (kennung === 'fmt ') {\n                kanaele = dv.getUint16(p + 10, true);\n                rate = dv.getUint32(p + 12, true);\n                block = dv.getUint16(p + 20, true);\n                tiefe = dv.getUint16(p + 22, true);\n            } else if (kennung === 'data') {\n                datenlaenge = Math.min(laenge, dv.byteLength - p - 8);\n            }\n            p += 8 + laenge + (laenge &#038; 1);\n        }\n        if (!rate) { return null; }\n        return { typ: 'wav', rate: rate, kanaele: kanaele, tiefe: tiefe,\n                 abtastwerte: block ? Math.floor(datenlaenge \/ block) : null };\n    }\n\n    function erkennen(puffer) {\n        var dv = new DataView(puffer);\n        if (dv.byteLength < 16) { return null; }\n        if (ascii(dv, 0, 4) === 'RIFF') { return wavLesen(puffer); }\n        if (ascii(dv, 0, 4) === 'fLaC') { return flacLesen(puffer); }\n        if (ascii(dv, 0, 4) === 'OggS') { return oggLesen(puffer); }\n        if (ascii(dv, 4, 4) === 'ftyp') { return mp4Lesen(puffer); }\n        if (ascii(dv, 0, 3) === 'ID3' || (dv.getUint8(0) === 0xFF &#038;&#038; (dv.getUint8(1) &#038; 0xE0) === 0xE0)) {\n            return mp3Lesen(puffer);\n        }\n        return null;\n    }\n\n    \/\/ ---- Auswertung --------------------------------------------------------\n    var DECODER_MP3 = 529;        \/\/ gemessene Verzoegerung der Decoder, siehe Text\n\n    function auswerten(info) {\n        if (!info) { return null; }\n        var rate = info.rate || 44100;\n        var a = { typ: info.typ, rate: rate, kanaele: info.kanaele || null,\n                  vorlauf: null, nachlauf: null, luecke_ms: null, urteil: 'nein', abtastwerte: info.abtastwerte || null };\n        if (info.typ === 'wav' || info.typ === 'flac') {\n            a.vorlauf = 0; a.nachlauf = 0; a.luecke_ms = 0; a.urteil = 'ja';\n        } else if (info.typ === 'mp3') {\n            if (info.vorlauf === null || info.vorlauf === undefined) {\n                a.urteil = 'nein';\n            } else {\n                a.vorlauf = info.vorlauf + DECODER_MP3;\n                a.nachlauf = info.nachlauf;\n                a.luecke_ms = (a.vorlauf + a.nachlauf) \/ rate * 1000;\n                a.urteil = 'mit_angaben';\n            }\n        } else if (info.typ === 'opus') {\n            a.vorlauf = info.vorlauf;\n            a.nachlauf = null;\n            a.luecke_ms = info.vorlauf \/ 48000 * 1000;\n            a.urteil = 'ja';\n            if (info.granule !== null &#038;&#038; info.granule !== undefined) {\n                a.abtastwerte = Math.max(0, info.granule - info.vorlauf);\n            }\n        } else if (info.typ === 'vorbis') {\n            a.vorlauf = 0; a.nachlauf = 0; a.luecke_ms = 0; a.urteil = 'ja';\n            if (info.granule) { a.abtastwerte = info.granule; }\n        } else if (info.typ === 'mp4') {\n            var vor = null, nach = null;\n            if (info.itunsmpb) { vor = info.itunsmpb.vorlauf; nach = info.itunsmpb.nachlauf; }\n            else if (info.elst &#038;&#038; info.elst.start > 0) { vor = info.elst.start; }\n            a.vorlauf = vor;\n            a.nachlauf = nach;\n            if (vor !== null) {\n                a.luecke_ms = (vor + (nach || 0)) \/ rate * 1000;\n                a.urteil = info.itunsmpb ? 'mit_angaben' : 'teils';\n            } else {\n                a.urteil = 'nein';\n            }\n        }\n        return a;\n    }<\/p>\n<p>    function felder(info, a) {\n        var liste = [];\n        function f(name, wert) { if (wert !== null && wert !== undefined && wert !== '') { liste.push([name, String(wert)]); } }\n        f(T.f_format, T['format_' + info.typ] || info.typ);\n        f(T.f_rate, (info.rate || a.rate) + ' Hz');\n        f(T.f_kanaele, info.kanaele);\n        if (info.typ === 'mp3') {\n            f(T.f_rahmen, info.rahmen || T.wert_keiner);\n            f(T.f_encoder, info.encoder);\n            f(T.f_vorlauf_roh, info.vorlauf === null ? T.wert_keiner : info.vorlauf + ' + ' + DECODER_MP3);\n            f(T.f_nachlauf_roh, info.nachlauf === null ? T.wert_keiner : info.nachlauf);\n        } else if (info.typ === 'opus') {\n            f(T.f_vorlauf_roh, info.vorlauf);\n            f(T.f_quellrate, info.quellrate ? info.quellrate + ' Hz' : null);\n            f(T.f_granule, info.granule);\n        } else if (info.typ === 'mp4') {\n            f(T.f_elst, info.elst ? text('elst_wert', { start: info.elst.start, dauer: info.elst.dauer }) : T.wert_keiner);\n            f(T.f_itunsmpb, info.itunsmpb ? text('itunsmpb_wert', { vorlauf: info.itunsmpb.vorlauf, nachlauf: info.itunsmpb.nachlauf }) : T.wert_keiner);\n        } else if (info.typ === 'flac') {\n            f(T.f_tiefe, info.tiefe ? info.tiefe + ' Bit' : null);\n        } else if (info.typ === 'vorbis') {\n            f(T.f_granule, info.granule);\n        } else if (info.typ === 'wav') {\n            f(T.f_tiefe, info.tiefe ? info.tiefe + ' Bit' : null);\n        }\n        if (a.abtastwerte) {\n            f(T.f_abtastwerte, a.abtastwerte);\n            f(T.f_dauer, zahl(a.abtastwerte \/ (a.rate || 44100), 3) + ' s');\n        }\n        return liste;\n    }<\/p>\n<p>    function bericht(info, a) {\n        var zeilen = [T.bericht_kopf];\n        felder(info, a).forEach(function (z) { zeilen.push(z[0] + ': ' + z[1]); });\n        zeilen.push(T.k_luecke + ': ' + (a.luecke_ms === null ? T.wert_unbekannt : zahl(a.luecke_ms, 1) + ' ms'));\n        zeilen.push(T['urteil_' + a.urteil]);\n        return zeilen.join('\\n');\n    }<\/p>\n<p>    \/\/ ---- Darstellung -------------------------------------------------------\n    function kachel(name, wert) {\n        var d = document.createElement('div');\n        d.className = 'gl-kachel';\n        var n = document.createElement('div'); n.className = 'gl-kachel-name'; n.textContent = name;\n        var w = document.createElement('div'); w.className = 'gl-kachel-wert'; w.textContent = wert;\n        d.appendChild(n); d.appendChild(w);\n        return d;\n    }<\/p>\n<p>    function darstellen(info, a) {\n        var kacheln = el('gl-kacheln'), urteil = el('gl-urteil'), tab = el('gl-felder');\n        if (!kacheln) { return; }\n        kacheln.textContent = '';\n        kacheln.appendChild(kachel(T.k_format, T['format_' + info.typ] || info.typ));\n        kacheln.appendChild(kachel(T.k_vorlauf, a.vorlauf === null ? T.wert_unbekannt : zahl(a.vorlauf \/ (a.rate || 44100) * 1000, 1) + ' ms'));\n        kacheln.appendChild(kachel(T.k_nachlauf, a.nachlauf === null ? T.wert_unbekannt : zahl(a.nachlauf \/ (a.rate || 44100) * 1000, 1) + ' ms'));\n        kacheln.appendChild(kachel(T.k_luecke, a.luecke_ms === null ? T.wert_unbekannt : zahl(a.luecke_ms, 1) + ' ms'));<\/p>\n<p>        urteil.className = 'gl-urteil gl-' + (a.urteil === 'mit_angaben' ? 'teils' : a.urteil);\n        urteil.textContent = '';\n        var kopf = document.createElement('div');\n        kopf.className = 'gl-urteil-kopf';\n        kopf.textContent = T['urteil_' + a.urteil];\n        var txt = document.createElement('div');\n        txt.className = 'gl-klein';\n        txt.textContent = T['urteil_text_' + a.urteil];\n        urteil.appendChild(kopf); urteil.appendChild(txt);<\/p>\n<p>        tab.textContent = '';\n        felder(info, a).forEach(function (z) {\n            var tr = document.createElement('tr');\n            var td1 = document.createElement('td'); td1.textContent = z[0];\n            var td2 = document.createElement('td'); td2.className = 'gl-wert'; td2.textContent = z[1];\n            tr.appendChild(td1); tr.appendChild(td2);\n            tab.appendChild(tr);\n        });\n        el('gl-ausgabe').hidden = false;\n        el('gl-fehler').hidden = true;\n        return a;\n    }<\/p>\n<p>    function fehlerZeigen(s) {\n        var f = el('gl-fehler');\n        if (!f) { return; }\n        f.textContent = s;\n        f.hidden = false;\n        el('gl-ausgabe').hidden = true;\n    }<\/p>\n<p>    var letztes = null;\n    function verarbeiten(puffer) {\n        var info;\n        try { info = erkennen(puffer); } catch (x) { info = null; }\n        if (!info) { fehlerZeigen(T.fehler_format); return null; }\n        var a = auswerten(info);\n        letztes = { info: info, a: a };\n        return darstellen(info, a);\n    }<\/p>\n<p>    function dateiNehmen(datei) {\n        if (!datei) { return; }\n        var leser = new FileReader();\n        leser.onload = function () { verarbeiten(leser.result); };\n        leser.onerror = function () { fehlerZeigen(T.fehler_lesen); };\n        try { leser.readAsArrayBuffer(datei); } catch (x) { fehlerZeigen(T.fehler_lesen); }\n    }<\/p>\n<p>    window.LW_TEST = window.LW_TEST || {};\n    window.LW_TEST.GL = {\n        T: T, erkennen: erkennen, mp3Lesen: mp3Lesen, mp4Lesen: mp4Lesen, oggLesen: oggLesen,\n        flacLesen: flacLesen, wavLesen: wavLesen, auswerten: auswerten, felder: felder,\n        bericht: bericht, darstellen: darstellen, verarbeiten: verarbeiten, zahl: zahl,\n        DECODER_MP3: DECODER_MP3\n    };<\/p>\n<p>    var zone = el('gl-zone'), eingabe = el('gl-datei'), knopf = el('gl-knopf');\n    if (!zone || !eingabe || !knopf) { return; }\n    knopf.addEventListener('click', function () { eingabe.click(); });\n    eingabe.addEventListener('change', function () { dateiNehmen(eingabe.files && eingabe.files[0]); eingabe.value = ''; });\n    ['dragenter', 'dragover'].forEach(function (typ) {\n        zone.addEventListener(typ, function (ev) { ev.preventDefault(); zone.classList.add('gl-ueber'); });\n    });\n    ['dragleave', 'drop'].forEach(function (typ) {\n        zone.addEventListener(typ, function (ev) { ev.preventDefault(); zone.classList.remove('gl-ueber'); });\n    });\n    zone.addEventListener('drop', function (ev) {\n        ev.preventDefault();\n        dateiNehmen(ev.dataTransfer && ev.dataTransfer.files && ev.dataTransfer.files[0]);\n    });\n    var kopieren = el('gl-kopieren'), kopiert = el('gl-kopiert');\n    if (kopieren) {\n        kopieren.addEventListener('click', function () {\n            if (!letztes) { return; }\n            var t = bericht(letztes.info, letztes.a);\n            if (navigator.clipboard && navigator.clipboard.writeText) {\n                navigator.clipboard.writeText(t).then(function () { kopiert.textContent = T.kopiert; },\n                    function () { kopiert.textContent = T.kopieren_fehler; });\n            } else {\n                kopiert.textContent = T.kopieren_fehler;\n            }\n        });\n    }\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reads an audio file in the browser and reports what the file itself declares about encoder delay and padding: the LAME tag in MP3, the edit list and iTunSMPB in MP4, the pre-skip in Opus, the sample count in FLAC. States how long the gap would be for a player that ignores those fields.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":38,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[92901],"class_list":["post-20054","page","type-page","status-publish","hentry","tag-music-production"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Gapless Playback Checker: encoder delay and padding in MP3, AAC, Opus and FLAC - Lukas Wojcik - Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Gapless Playback Checker: encoder delay and padding in MP3, AAC, Opus and FLAC - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Reads an audio file in the browser and reports what the file itself declares about encoder delay and padding: the LAME tag in MP3, the edit list and iTunSMPB in MP4, the pre-skip in Opus, the sample count in FLAC. States how long the gap would be for a player that ignores those fields.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-21T20:30:53+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gapless-playback-encoder-delay-checker\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gapless-playback-encoder-delay-checker\\\/\",\"name\":\"Gapless Playback Checker: encoder delay and padding in MP3, AAC, Opus and FLAC - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-09-21T20:29:11+00:00\",\"dateModified\":\"2026-09-21T20:30:53+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gapless-playback-encoder-delay-checker\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gapless-playback-encoder-delay-checker\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gapless-playback-encoder-delay-checker\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Gapless Playback Checker: encoder delay and padding in MP3, AAC, Opus and FLAC\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Gapless Playback Checker: encoder delay and padding in MP3, AAC, Opus and FLAC - Lukas Wojcik - Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/","og_locale":"en_US","og_type":"article","og_title":"Gapless Playback Checker: encoder delay and padding in MP3, AAC, Opus and FLAC - Lukas Wojcik - Blog","og_description":"Reads an audio file in the browser and reports what the file itself declares about encoder delay and padding: the LAME tag in MP3, the edit list and iTunSMPB in MP4, the pre-skip in Opus, the sample count in FLAC. States how long the gap would be for a player that ignores those fields.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/","og_site_name":"Lukas Wojcik - Blog","article_modified_time":"2026-09-21T20:30:53+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/","name":"Gapless Playback Checker: encoder delay and padding in MP3, AAC, Opus and FLAC - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-09-21T20:29:11+00:00","dateModified":"2026-09-21T20:30:53+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gapless-playback-encoder-delay-checker\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/"},{"@type":"ListItem","position":3,"name":"Gapless Playback Checker: encoder delay and padding in MP3, AAC, Opus and FLAC"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/20054","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=20054"}],"version-history":[{"count":1,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/20054\/revisions"}],"predecessor-version":[{"id":20057,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/20054\/revisions\/20057"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/38"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=20054"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=20054"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}