# ECMAScript Embedded Bitmap Encoding

I propose the following bitmap format, suitable for embedding small images in TypeScript or JavaScript source code:

```javascript
// ECMAScript Embedded Bitmap Encoding (EEBE)
// Required fields:
export const lines = [
  0b0001000,
  0b0111000,
  0b1101000,
  0b1001000,
  0b0001000,
  0b0001110,
  0b0001111,
  0b0001111,
  0b0000110,
]
export const width = 7
export const bpp = 1
// Optional fields:
export const palette = [
  0x000,
  0xfff,
]
```

Let's call it *ECMAScript Embedded Bitmap Encoding* (EEBE). EEBE is a format used to store bitmap images within ECMAScript code in an efficient manner. It is designed to be compact and easy to parse, suitable for scenarios with hard size constraints such as [js13kGames](https://js13kgames.com/), and isn't at all intended to be a general purpose image format.

An EEBE file is an ECMAScript module that exports the following fields:

* `lines`: an array of integers, each representing a scanline of the image. Each integer is a bitfield of size `width * bpp`, with the least significant bit(s) representing the **leftmost** pixel of the scanline.
    
* `width`: the width of the image in pixels.
    
* `bpp`: the number of bits per pixel.
    
* `palette`: an optional array of integers, each representing a color. The number of colors in the palette should equal `2 ** bpp`. Nullish values are treated as transparent. If this field is omitted, the rendering is implementation-defined.
    
* Any other fields, depending on the implementation.

Optimized for size, the above example is 116 bytes long (99 bytes gzipped) and produces the following image:

[![](https://cdn.hashnode.com/res/hashnode/image/upload/v1675779459552/5d4f8957-995f-4181-9a85-7dd630b7a9c8.png)][codepen]

[EEBE Playground][codepen]

[codepen]: https://codepen.io/mvasilkov/full/WNKmPqR

```javascript
// This is the same example as above, written in a compact form.
export const lines=[8,56,104,72,8,14,15,15,6]
export const width=7
export const bpp=1
export const palette=[0,4095]
```

The footprint can be further reduced during build:

* Using a module bundler such as [Rollup][1] will eliminate the `export` statements.
* Running a minifier (e.g. [Terser][2]) will shorten the field names and inline some of the constants.

[1]: https://rollupjs.org/
[2]: https://terser.org/

It's also possible to save bytes by using 12-bit color (`#9d5` instead of `#99dd55`) and sharing the palette between multiple images.

## Decoding the image

An EEBE image can be decoded as follows:

```javascript
function readBitmap(lines, width, bpp, readFunction) {
  const shift = 1 << bpp
  for (let y = 0; y < lines.length; ++y) {
    for (let x = 0; x < width; ++x) {
      const value = lines[y] / shift ** x & shift - 1
      readFunction(x, y, value)
    }
  }
}
```

(This function is available in [natlib][3].)

[3]: https://github.com/mvasilkov/natlib/blob/master/typescript/bitmap/bitmap.ts

For each pixel in the image, the `readFunction(x, y, value)` is invoked. The pixel's `value` is an integer in the range `[0, 2 ** bpp - 1]` that can be used to retrieve the corresponding color from the palette.

You might be wondering why the `readBitmap` function uses the division operator instead of a `<<` bit shift. It's because the bitwise operations in JS truncate their operands to 32 bits, whereas the Number type is a 64-bit floating point, allowing for integer values of up to 53 bits. So the tradeoff here is that we can either

* Enjoy the full 53-bit stride for images, but use the (slower) division operator, or
* Use the fast `<<` bit shift operator, but only get 32 bits of stride.

## Real-world usage

I've used this format in my js13kGames entries, including [The Neatness][4], with good results.

[4]: https://js13kgames.com/entries/the-neatness

It's clear that variations of this — storing bitmaps as `int[]` — have been in use since at least ZX Spectrum days. The motivation for writing this short spec and giving EEBE its name is to promote interoperability, not to claim originality.

## Questions

Absolutely none of these questions have been asked.

### What about extensibility?

You can include extra fields. For example, in [The Neatness](https://mvasilkov.animuchan.net/the-neatness-js13kgames2022) levels contain hotspots (entry and exit points) in addition to the level geometry represented by the bitmap.

### Where's the tooling?

It's in the planning phase.

### How to pronounce "EEBE"?

Try `[jebi]`.

### Why did the chicken cross the road?

To gather intelligence. It was a [Chinese surveillance chicken][naomi].

[naomi]: https://twitter.com/RealSexyCyborg/status/1621323329721487360

