What's actually inside an MP3 tag: ID3v1, ID3v2.3 and ID3v2.4 in hex
People talk about "the tags" as if they were a property of the song. They're not. They're a small block of bytes stapled to the front of the file, and sometimes another one stapled to the back. Here's what those bytes look like in a real file, because once you've seen them, half the strange behaviour of music players stops being mysterious.
The test file
I took a 15-second MP3 with no tag at all (93,024 bytes), wrote a small ID3v2.3 tag on the front and an ID3v1 tag on the back, and dumped the bytes. The tag says: title Morning Raga, artist Swara, album Test Album, year 2026, track 3 of 10, genre Rock, and a comment. This is the same file you can download from this site and drop into the editor.
The front: ID3v2.3
Here are the first 96 bytes. Left column is the offset, middle is hex, right is the same bytes as text where they're printable.
00000000 49 44 33 03 00 00 00 00 01 74 54 49 54 32 00 00 ID3......tTIT2..
00000010 00 1d 00 00 01 ff fe 4d 00 6f 00 72 00 6e 00 69 .......M.o.r.n.i
00000020 00 6e 00 67 00 20 00 52 00 61 00 67 00 61 00 00 .n.g. .R.a.g.a..
00000030 00 54 50 45 31 00 00 00 0f 00 00 01 ff fe 53 00 .TPE1.........S.
00000040 77 00 61 00 72 00 61 00 00 00 54 52 43 4b 00 00 w.a.r.a...TRCK..
00000050 00 0d 00 00 01 ff fe 33 00 2f 00 31 00 30 00 00 .......3./.1.0..
Reading it left to right:
49 44 33 is the text "ID3". Every player checks for these three bytes first. 03 00 is the version: 2.3, revision 0. Then a flags byte (00, nothing special) and four bytes of size, 00 00 01 74. That size uses only seven bits of each byte, a scheme called syncsafe, so it reads as 1×128 + 0x74 = 244 bytes of tag after this 10-byte header. 254 in total, which is what my tagging library reported.
Then the frames. Each one is a four-letter name, four bytes of size, two bytes of flags, and the content. TIT2 is the title. Its size is 1d = 29 bytes, and that's exactly what follows: one byte saying which text encoding is used (01 = UTF-16), a two-byte byte-order mark (ff fe), then "Morning Raga" at two bytes per letter, then two zero bytes to end the string. 1 + 2 + 24 + 2 = 29.
The same pattern repeats: TPE1 is the artist, TRCK the track number (stored as the text "3/10", not as a number), and further on TALB, TCON, TYER and COMM. The fields guide has the full list of names and what they're for.
That encoding byte is the whole story behind titles that show up as question marks. 00 means Latin-1, which can only hold Western European letters. 01 means UTF-16, which holds everything. A tagger that writes 00 for a Hindi or Russian title has already lost it. More on that.
The back: ID3v1
Now the last 128 bytes of the same file.
00016c5e 54 41 47 4d 6f 72 6e 69 6e 67 20 52 61 67 61 00 TAGMorning Raga.
00016c6e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00016c7e 00 53 77 61 72 61 00 00 00 00 00 00 00 00 00 00 .Swara..........
00016c8e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 54 ...............T
00016c9e 65 73 74 20 41 6c 62 75 6d 00 00 00 00 00 00 00 est Album.......
00016cae 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 32 .............202
00016cbe 36 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6...............
00016cce 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 11 ................
This one has no sizes and no names, because every field is at a fixed position. "TAG", then 30 bytes for the title, 30 for the artist, 30 for the album, 4 for the year, 28 for the comment, one zero byte, one byte for the track number (03) and one byte for the genre (11 = 17 = Rock, from a fixed list). Unused space is zero bytes. That's the entire format, designed in 1996, and it's why an old car stereo shows "Original Motion Picture Soundt" and no cover: 30 characters is all it has, and there's nowhere to put a picture.
It lives at the end because the format was bolted on after MP3 already existed. A player that doesn't know about it just plays 128 bytes of near-silence, which most decoders skip anyway.
Where 2.4 differs, and why 2.3 is still the default
ID3v2.4 came out in 2000 and tidied a few things up. Frame sizes became syncsafe like the header. UTF-8 was allowed as a text encoding (code 03). The year frame TYER was replaced by a full timestamp frame, TDRC. A tag can optionally go at the end of the file instead of the front.
All sensible, and all irrelevant to the reason people still write 2.3: for years, Windows Media Player, Windows Explorer's file properties and a great many car stereos read 2.3 correctly and made a mess of 2.4. The desktop taggers picked 2.3 as their default to avoid the support email, and there it stayed. Modern players read both. Old hardware reads 2.3. So 2.3 is the version that never causes a problem, and it's what the editor here writes.
There's also 2.2, with three-letter frame names, which old versions of iTunes wrote. You'll still meet it in libraries that were ripped a long time ago. Re-saving the file with any modern tagger upgrades it.
What this means when something looks wrong
A title cut at 30 characters means the player is reading the ID3v1 block and ignoring the front. A missing cover on a device that shows covers for other files usually means the tag is 2.4 or 2.2 and the device only reads 2.3. Question marks in a title mean a Latin-1 encoding byte in front of text that isn't Latin. And a file that's exactly 128 bytes longer than you expected has an ID3v1 tag on it, whether or not anything displays it.
None of this touches the audio. The bytes from the end of the front tag to the start of the back tag are the sound, and a tag editor copies them across without looking at them. That's why re-tagging never costs quality, and why the saved file's size is the old size plus or minus the tags and nothing else.