Compressing a flag to 11 bits✍️My Blog/Compressing a flag to 11 bitsSearchCompressing a flag to 11 bitsTagsPublishedSeptember 12, 2026AuthorI recently saw an interesting YouTube video by “Physics for the Birds”. The main video is about matrixes, but he used flags to explain some of the operations on matrixes:Screenshot from the videoHe broke down how you could break down flags across its axes to represent them as matrixes, and save some storage space. In the end, this was used to circle back to actual matrixes and calculations on them.However this inspired me: Flags are often so simple - just some colored stripes, common elements like a star, crescent or cross. Using some custom encoding scheme, shouldn’t defining the 🇫🇷French flag like “3 stripes, Blue, White, Red” with some custom decoder and renderer only take a handful of bits? How would such an encoding look like?So I tried to create one:RequirementsTo start, I put down some acceptance criteria for my encoding:The decoded flag has to be "recognizable enough”Details don't have to be exactly correct. Small deviations, inaccuracies are ok - as long as somebody can look at it and say "Ah that's that flag!"This includes things like the exact placement, shape details of objects.This also includes the exact color. I know how proud France is of their new shade of blue, but for this encoding we can break down any blue tone into some middle blueOnly country flagsNo state flags, city flags, or other vexillological designsNo 🇳🇵NepalSorry Nepal, I like your non-rectangular flag but this would just complicate stuffSimple flags should require very little bits, while very intricate flags are allowed to take up some more bits - so no fixed lengthNo coat of armsFlags like 🇦🇩 Andorra contain their own coat of arms, for which you pretty much need some custom bitmap/vector to render it. That would just put some SVG or something in the tiny format so no What makes a flag a flag?To start, I needed to figure out what elements the common flag has. Worldometers List of all country flags was very helpful for this:First thing: Damn, flags are more varied than I thought. Of course you have the simple striped ones like 🇫🇷France, 🇮🇹Italy or 🇩🇪Germany. Then you have designs like 🇧🇮Burundi or 🇧🇦Bosnia with stars, sections and everything. 🇲🇰North Macedonia and 🇸🇨Seychelles with radial stripes. 🇨🇿Czechia, 🇧🇸Bahamas and more with custom triangles at the left edge and so many more.So what do most flags have in common. From what I see:StripesStripes, lot’s of stripes: 🇫🇷Horizontal, 🇩🇪Vertical, 🇵🇱 2 Stripes, 🇦🇲3 Stripes, 🇺🇸13 Stripes, 🇨🇴 Uneven stripes, 🇨🇬Angled StripesCommon Shapes🇻🇳Stars, 🇩🇿Crescents, 🇯🇵CirclesAt custom sizes and positions - 🇻🇳one or 🇺🇸manyLeft color triangleSuprisingly common and in many colors - but often the same general shape 🇰🇲🇧🇸🇨🇿Top left corner coloredSome kind of custom rectangle happening in the top left 🇺🇸🇬🇷Union Jack🇬🇧🇦🇺🇳🇿🇹🇻And then the nordic countriesThey’re definitely a big plus to have… 🇳🇴🇸🇪🇩🇰🇫🇮Breaking down the flagFrom what I can tell, the protocol should define these elements:Aspect RatioFlags all have their individual aspect ratios but there are clear patterns: around 45% of flags have the ratio 2:3, around 28% have 1:2, around 9% have 3:5 - and then some longtail of other formatsColor paletteAs defined, we’ll focus on general color tones instead of relicating the exact color code. So we’ll focus on color groups like Blue, Green, YellowOnce again we have a similar picture: Surpisingly a lot of red, then white, blue, yellow/gold, green, black and orange - and then a longtail or other colorsLayersI think it makes sense to define the actual contents using a handful of “Layers” instead of trying to hardcode all the common things separately. Each layer can then define its own list of options to supplyThis should work like in Photoshop or other apps: e.g. for the 🇺🇸US Flag you’d first have the stripe layer, then a layer for the blue rectangle, then a star layerThe most common layer will be the “Stripe” layer. This can have options for the amount of stripes and its colors, the direction, even or uneven distribution, and repeating patternsOther layers should be a “Shapes” layer for stars, crecents and others - defining the position and rotation. A “Band” layer and a “Region” layer to color a specific rectangleTurning it into bitsAs with most things, so many aspects of flags also seem to follow Zipf’s Law - aspect ratio, colors used, elements on it (Stripes, stars etc.). To keep common elements short I thus decided to encode almost everything into its own Huffman Tree, assigning nice short binary codes for the common cases.To allow the longtail to exist without creating a huge tree, I decided to cut off all values that only exist in a single flag and instead set the last leaf of the tree to a “Custom” value, followed by a fixed-length free value option. For example, this allowed 🇸🇻El Salvador to keep its 189:335 aspect ratio without needing that in the tree itself.As an example, here is the full Huffman tree for the flag aspect ratio:graph TD %% Internal Nodes root((Root)) n1(( )) n11(( )) n110(( )) n1101(( )) n11011(( )) n111(( )) n1110(( )) n11100(( )) n111001(( )) n11101(( )) n111010(( )) n111011(( )) n1111(( )) n11110(( )) n111100(( )) n111101(( )) n11111(( )) n111110(( )) n1111100(( )) n1111101(( )) n111111(( )) n1111110(( )) n1111111(( )) n11111111(( ))
%% Leaf Nodes (Ratios) L_2_3[2:3] L_1_2[1:2] L_3_5[3:5] L_5_8[5:8] L_10_19[10:19] L_3_4[3:4] L_4_7[4:7] L_1_1[1:1] L_7_10[7:10] L_8_11[8:11] L_11_18[11:18] L_11_20[11:20] L_11_28[11:28] L_18_25[18:25] L_1_phi[1:φ] L_4_5[4:5] L_6_7[6:7] L_10_17[10:17] L_13_15[13:15] L_15_22[15:22] L_16_25[16:25] L_189_335[189:335] L_28_37[28:37] L_5_7[5:7] L_7_11[7:11] L_CUSTOM[CUSTOM]
%% Left Branch (0...) root -- 0 --> L_2_3 root -- 1 --> n1 n1 -- 0 --> L_1_2 n1 -- 1 --> n11 %% 110... Branch n11 -- 0 --> n110 n110 -- 0 --> L_3_5 n110 -- 1 --> n1101 n1101 -- 0 --> L_5_8 n1101 -- 1 --> n11011 n11011 -- 0 --> L_10_19 n11011 -- 1 --> L_3_4
%% 111... Branch n11 -- 1 --> n111 n111 -- 0 --> n1110 %% 1110... Sub-branches n1110 -- 0 --> n11100 n11100 -- 0 --> L_4_7 n11100 -- 1 --> n111001 n111001 -- 0 --> L_1_1 n111001 -- 1 --> L_7_10 n1110 -- 1 --> n11101 n11101 -- 0 --> n111010 n111010 -- 0 --> L_8_11 n111010 -- 1 --> L_11_18 n111011 -- 0 --> L_11_20 n111011 -- 1 --> L_11_28 n11101 -- 1 --> n111011
%% 1111... Branch n111 -- 1 --> n1111 n1111 -- 0 --> n11110 %% 11110... Sub-branches n11110 -- 0 --> n111100 n111100 -- 0 --> L_18_25 n111100 -- 1 --> L_1_phi n1111001(( )) n11110 -- 1 --> n111101 n111101 -- 0 --> L_4_5 n111101 -- 1 --> L_6_7
%% 11111... Branch n1111 -- 1 --> n11111 n11111 -- 0 --> n111110 %% 111110... Sub-branches n111110 -- 0 --> n1111100 n1111100 -- 0 --> L_10_17 n1111100 -- 1 --> L_13_15 n111110 -- 1 --> n1111101 n1111101 -- 0 --> L_15_22 n1111101 -- 1 --> L_16_25
%% 111111... Deepest Branch n11111 -- 1 --> n111111 n111111 -- 0 --> n1111110 n1111110 -- 0 --> L_189_335 n1111110 -- 1 --> L_28_37 n111111 -- 1 --> n1111111 n1111111 -- 0 --> L_5_7 n1111111 -- 1 --> n11111111 n11111111 -- 0 --> L_7_11 n11111111 -- 1 --> L_CUSTOM
%% Styling for better readability classDef leaf fill:#e1f5fe,stroke:#0288d1,stroke-width:2px; classDef internal fill:#eceff1,stroke:#607d8b,stroke-width:1px; class L_2_3,L_1_2,L_3_5,L_5_8,L_10_19,L_3_4,L_4_7,L_1_1,L_7_10,L_8_11,L_11_18,L_11_20,L_11_28,L_18_25,L_1_phi,L_4_5,L_6_7,L_10_17,L_13_15,L_15_22,L_16_25,L_189_335,L_28_37,L_5_7,L_7_11,L_CUSTOM leaf; class root,n1,n11,n110,n1101,n11011,n111,n1110,n11100,n111001,n11101,n111010,n111011,n1111,n11110,n111100,n111101,n11111,n111110,n1111100,n1111101,n111111,n1111110,n1111111,n11111111 internal; So for the 45% of flags with an aspect ratio of 2:3, all that’s needed is setting the first bit to 0. The format then contains Huffman trees for:Aspect Ratio (Most common: 2:3, 1:2, 3:5)Custom Width:Height for longtailColor Palette size (Most common: 3, 2, 4, 5)Custom “Count - 7” for longtail, since the tree goes up to 7Color (Most common: Red, White, Blue)Custom colors can be given as compact 10-bit RGB approximation (RRR GGGG BBB)Number of layersLayer Type (Stripes, Shape, Regions, Cross, Band, Buildin)Some more specialized subtreese.g. Number of points on the star, shape placementThe whole format is pretty much just one Huffman tree walk after the other to define what the flag is made of. With this, we can try encoding our first flag into the format. For this, I’ll take 🇮🇩Indonesia, as its definitely our encoding winner, or I guess “The most average flag”:A 2:3 aspect ratio (1 bit to encode) → 0the most common aspect ratioA color palette size of 2 colors (2 bits) → 10This is the only place it looses a bit, since 3 colors is the most common in the treeThen definining the 2 colors: Red (2 bits) → 00 and white (2 bits) → 01the 2 top-most colors in the treeA layer count of 1 layer (1 bit) → 0top of the treeA stripe layer (1 bit) → 0, “palette-equal” mode (i.e. give every color in the palette one equal stripe, 1 bit) → 0 and horizontal stripes (1 bit) → 0All the top of their Huffman tree→ All combined: 0 10 00 01 0 0 0 0, or base64 encoded “QgA=”Using this format, the average flag can be represented in 76 bits, with a median of 55 bits. The longest one is 🇶🇦Qatar at 420bits: #gHR1Y$?-+]m.0xS3F!0{.UH{uDppW5u2^+s|6~(p@GwHH<N:?57K99\(s)~!G4`! . I’d say that flag can only barely be encoded into our format: It has a zig-zagged edge between the sides, which I encoded as 11 individual rectangle layers.Union Jack 🇬🇧You could say I cheated a little here. The union jack is so common but so complicated to build from layers that I just put the whole thing as just a build-in shape in the protocol. So instead of building it, a flag can just specify “Union Jack Layer in the lop left” to place it.Encoding itI first used base64 to simply turn the bits into saveable text. With its 6 payload bits per ASCII byte, the average flag definition needed 14 characters with a median of 12 characters. The shortest flag code is “QgA=” for Indonesias flag.However, I decided to use Base94, which uses every visible one-byte ASCII character from “!” through “~” to try improving the efficiency of encoding some more.I also thought of some Emoji-based encoding, but since every character would require more than 1 byte to save there, the reduced character count would probably still require mote bits in total. (Also I didn’t want to risk encoding a countries flag as “💩🤮👎” or something like that…)With this, we’re down to an average of 12 characters for a flag, with a median of 9 characters. Indonesia stays the shortest encoding: “<F”.Rendering itI used ChatGPT Codex to turn the format into a two-step system: A encoder/decoder and an SVG renderer.The decoder first turns the binary blob into a readable format. For 🇮🇩Indonesias flag we already broke down, this looks like this:{ "aspectRatio": { "kind": "rational", "height": "2", "width": "3" }, "palette": [ { "r": 210, "g": 16, "b": 52 }, { "r": 255, "g": 255, "b": 255 } ], "layers": [ { "kind": "stripes", "direction": "horizontal", "stripes": [ { "color": 0 }, { "color": 1 } ] } ] }The renderer then takes this decoded flag and turns it into an SVG code like this:<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1.5 1"> <rect x="0" y="0" width="1.5" height="0.5" fill="#d21034"/> <rect x="0" y="0.5" width="1.5" height="0.5" fill="#fff"/> </svg>The code behind it is pretty nice, with some separate class and interfaces for the encoded binary parts and layers. However, even tsc-compiled the encoder/decoder sits at 27kB and the renderer at 12.5kB - maybe a bit much after just optimizing every bit of the source format…So I went back to Codex to create an alternative “minidecoder”: Combining decoding and rendering into one pass instead of two separate engines, getting rid of the nice interface/class infrastructure, and just boiling everything down to small primitive functions.WIth this, we got down to one 470-line TypeScript file, 5.29 kB (2.66 kB gzipped) when compiled.Flags that aren’t coveredWith this (rather primitive) format I managed to encode 128 flags with varying success.However, there we also 67 flags that I couldn’t manage to encode. Some notable reasons:🇪🇸 Spain, 🇬🇶 Equatorial Guinea, 🇦🇩 Andorra, 🇧🇿 Belize, 🇧🇳 Brunei, 🇰🇭 Cambodia, 🇨🇷 Costa Rica, 🇭🇷 Croatia, 🇩🇴 Dominican Republic, 🇪🇨 Ecuador, 🇸🇻 El Salvador, 🇫🇯 Fiji, 🇭🇹 Haiti, 🇲🇽 Mexico, 🇲🇩 Moldova, 🇲🇪 Montenegro, 🇳🇮 Nicaragua, 🇴🇲 Oman, 🇵🇾 Paraguay, 🇵🇹 Portugal, 🇸🇲 San Marino, 🇷🇸 Serbia, 🇸🇰 Slovakia, 🇸🇮 Slovenia, 🇻🇪 VenezuelaContain a coat of arms, seal or national emblem (25)🇦🇴 Angola, 🇧🇧 Barbados, 🇸🇿 Eswatini, 🇬🇹 Guatemala, 🇰🇪 Kenya, 🇱🇸 Lesotho, 🇱🇮 Liechtenstein, 🇲🇹 Malta, 🇲🇿 Mozambique, 🇹🇯 Tajikistan, 🇻🇦 Vatican CityObject glyphs such as weapons, tools, crowns, shields or a hat (11)🇦🇱 Albania, 🇧🇹 Bhutan, 🇩🇲 Dominica, 🇪🇬 Egypt, 🇰🇮 Kiribati, 🇵🇬 Papua New Guinea, 🇱🇰 Sri Lanka, 🇺🇬 Uganda, 🇿🇲 Zambia, 🇿🇼 ZimbabweAnimal glyphs, mostly eagles and other birds (10)🇨🇦 Canada, 🇨🇾 Cyprus, 🇪🇷 Eritrea, 🇬🇩 Grenada, 🇱🇧 LebanonPlant glyphs such as leaves, branches or a nutmeg (5)🇦🇬 Antigua and Barbuda, 🇧🇷 Brazil, 🇳🇵 Nepal, 🇿🇦 South Africa, 🇻🇺 VanuatuGeometry the layer model cannot express, such as Y-shapes, V-fields, a globe or a non-rectangular outline (5)🇦🇫 Afghanistan, 🇮🇷 Iran, 🇮🇶 Iraq, 🇸🇦 Saudi ArabiaArabic text or calligraphy (4)Adding some custom Text layer could help here🇮🇳 India, 🇰🇬 Kyrgyzstan, 🇲🇳 Mongolia, 🇰🇷 South KoreaReligious or cultural symbols such as the Ashoka Chakra, tunduk, Soyombo or Taegeuk (4)🇧🇾 Belarus, 🇰🇿 Kazakhstan, 🇹🇲 TurkmenistanOrnamental hoist patterns→ So pretty much all contain some custom glyph or text that’s not easily representable with out geometry-based layersRandom flags!Now we have kind of a structured language of what a flag might look like, so I decided to add a randomizer to fill a new flag with random items from the various Huffman trees we have.…yeah I guess I can see some country having a flag like this? All in allI’m sure others can find even more bit-saving methods or completely different ways of compressing or laying out the data. However it was still quite a lot of fun to go from a list of all flags, finding common elements and searching for ways to pack as much data as possible into the bits. Finally getting to use my Huffman Coding knowledge from my Bachelors and writing some bit-level code was also very fun. You can find the final page with the flags on https://vantezzen.github.io/miniflags/ and the source code at . Please don’t expect super clean code there - in the end it was mostly vibe coded based on my format ideas. All details about the format are documented at .Table of ContentsRequirementsWhat makes a flag a flag?Breaking down the flagTurning it into bitsUnion Jack 🇬🇧Encoding itRendering itFlags that aren’t coveredRandom flags!All in allCopyright 2026 vantezzen |
The author explored the theoretical possibility of compressing country flags into a minimal number of bits by treating them as structured data, inspired by matrix decomposition techniques. The fundamental idea is to define the essential visual elements of a flag—such as color, shape, and arrangement—and encode these features using custom schemes to minimize storage space.
To establish the encoding protocol, the author set specific requirements, prioritizing recognizability over absolute accuracy, allowing for slight deviations in details. This involved abstracting color into general tones and excluding complex vexillological designs like coats of arms, focusing instead on common elements like stripes, shapes, and color palettes found across various national flags. A thorough analysis of existing flags revealed common recurring elements, including various stripe patterns, geometric shapes, specific color combinations, and unique regional features.
The proposed method for breaking down a flag involves defining hierarchical layers, similar to software design structures, where each layer defines a set of options for the elements present. For instance, a "Stripe" layer could specify the number, direction, and distribution of colors, while a "Shapes" layer could define the placement and rotation of elements like stars or crescents. This layering approach aims to separate common structural elements from unique national identifiers, mimicking how complex graphics are managed in applications like Photoshop.
To translate these layered attributes into binary data efficiently, the author utilized Huffman coding. By observing that flag properties often follow Zipf's Law, the author generated a Huffman tree for common attributes like aspect ratios and color palettes, assigning shorter binary codes to the most frequent categories. To accommodate the diverse set of flag dimensions, the encoding scheme was modified to allow for less common values by designating a "Custom" leaf node, effectively handling the longtail of less frequent data within the tree structure.
The encoding process was further refined by experimenting with different methods to represent the resulting binary stream in a compact, human-readable format. While initial attempts used Base64, the author investigated Base94 and emoji-based encoding to minimize the character count required for representation, ultimately seeking the shortest possible string for the flag's data.
The process was then realized through a two-step system: an encoder/decoder and a renderer. The decoder translates the binary data back into structured information, specifying aspects like aspect ratio, color palette, and visual layers. The renderer then uses this decoded data to generate vector graphics, such as Scalable Vector Graphics, that depict the flag. Optimization efforts led to a reduction in complexity by merging the decoding and rendering functions into a single, more primitive implementation, which resulted in a highly compact system.
The exploration extended to identifying specific classes of flags that proved challenging to encode with sufficient efficiency. These unrepresented categories included flags featuring national emblems, object glyphs (weapons, tools), animal glyphs, plant glyphs, and custom text or cultural symbols. This analysis demonstrated that while the structured language successfully captured geometric and color data, encoding highly ornate or text-heavy national symbols requires additional, specialized data fields or layers. The work culminates in a framework for variable-length, layer-based compression of flag data, demonstrating a method for information density optimization in visual representation. |