Browsers will decode an .ico for you, but you get one image back. We wanted every image in the file, so we wrote the parser ourselves in TypeScript. The format is small, and it still had plenty of surprises.

  1. Zero means 256 Each image has a directory entry with its width and height in single bytes. A byte can't hold 256, so 0 means 256.
  2. The same bytes mean different things Bytes 4 to 7 of an entry are the color planes and bits per pixel in an icon, but the hotspot X and Y in a cursor. The two formats differ only by a type field in the header (1 or 2). That's why a .ico renamed to .cur loads with a nonsense hotspot: Windows reported 1, 32 for a 32 px icon in our test.
  3. There are two image kinds Since Vista, an entry can be a complete PNG, which you can copy out byte for byte. Otherwise it's BMP-style rows in 1, 4, 8, 24 or 32 bits, stored bottom-up and followed by a 1-bit AND mask.
  4. All-zero alpha means opaque A 32-bit image can carry an alpha channel. But if every alpha value is 0, the icon counts as opaque and the mask applies. Skip that rule and some icons come out invisible.
  5. Inverted pixels can't be represented Classic cursors can mark pixels that invert whatever is beneath them. PNG can't express that, so we make them transparent, as Chrome does.
  6. Canvas isn't exact for partial transparency Browsers store canvas pixels premultiplied, so partly transparent pixels can shift slightly. We wrote our own PNG encoder using the Compression Streams API to keep exact values, with canvas as a fallback. How we tested it We ran the parser over every cursor in a Windows 11 PC's Cursors folder plus three app icons: 186 files holding 940 images. Every image matched ImageMagick's reading pixel for pixel. Median time was 3.9 ms per file, run outside the browser. You can try it with flipconverto.com/convert/ico-to-png, which runs entirely client-side. Disclosure: FlipConverto is our site. Further reading: Raymond Chen's "The evolution of the ICO file format" series (https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513). Note: this article was drafted with AI assistance and checked against our own test results.