Amp98 notes

Rebuilding Winamp's skin engine natively for the Mac

A Winamp 2 skin is a zip file full of Windows bitmaps. MAIN.BMP is the 275 by 116 pixel main window. CBUTTONS.BMP holds every transport button in its normal and pressed state. TEXT.BMP is a 5 by 6 pixel font. NUMBERS.BMP is the clock. There is no layout file: the positions of every sprite were hard coded into Winamp itself in the late 1990s, and every skin artist since then has drawn into those exact rectangles.

Amp98 is a native macOS player that reads those files and draws them exactly as their artists made them. This post is about what that took.

The coordinates are the spec

Because the layout lives in the player, not in the skin, getting the coordinates right is the whole job. I did not guess them. The Webamp project (MIT licensed) has spent years documenting every sprite rectangle, so I used its table as the reference and then rendered real skins to check the result.

Each window is a borderless NSWindow that blits sprites with nearest neighbour sampling, so a 1 pixel line in a 1999 skin is still exactly 1 pixel, or exactly a 2 by 2 block on a Retina screen. Double size mode makes it a 4 by 4 block. Nothing is smoothed.

Old files are weirder than the format

The format is simple. The files are not.

My test set is 142 popular classic skins: 138 of the most downloaded skins in the Internet Archive's Winamp collection plus the four demo skins that ship with Webamp. All 142 load and render.

Modern skins have a programming language

Winamp 3 and 5 introduced "modern" .wal skins: XML layouts, PNG layers, and scripts compiled to bytecode called MAKI. A modern skin can be any shape, slide drawers out of a head, or rebuild its layout when you click a button. Without the scripts, a modern skin is a pile of overlapping layers.

So Amp98 includes a MAKI virtual machine written in Swift: a bytecode parser, a stack interpreter, and enough of Winamp's standard object library (groups, layers, buttons, sliders, text, timers, animated targets) to run real skins. Scripts are sandboxed by a step budget, so a broken skin cannot hang the player.

Of the 27 modern skins in my test set, 26 load. The one that does not depends on plugin files that were never inside the skin. Modern skin support is labelled beta, because the long tail of that library is long.

Audio

Amp98 plays your own files through AVAudioEngine, with a 10 band equalizer and an FFT spectrum analyzer modelled on the original.

A small surprise from the audio side: macOS happily decodes Ogg Vorbis and Opus, but AVURLAsset reports the wrong duration for them. A 60 second Opus file came back as 75 seconds. Reading the frame count from AVAudioFile gives the exact length, so the playlist uses that instead.

A skin museum in the app

There are more than 100,000 classic skins in the Winamp Skin Museum at skins.webamp.org. Amp98 has a browser for it built in: search, click a skin, and it downloads and applies. Skins belong to the people who drew them, so the app only fetches them from the museum when you ask.

Try it

Amp98 is $9.99, one-time, for macOS 14 and later on Apple silicon and Intel: https://amp98.com

Thanks to Jordan Eldredge and the Webamp contributors, whose years of reverse engineering made the coordinate tables and the MAKI format documentation possible.