Карта мира за 500 байт: магия deflate-сжатия и нативного JavaScript

Что, если я скажу вам, что полноценную ASCII-карту всего мира — со всеми континентами и океанами — можно уместить буквально в несколько сотен байт? Именно это сделал разработчик Иво Кадзела (Iwo Kadziela) при помощи ИИ-ассистента OpenAI Codex. Результат — карта, которая занимает 445 байт сжатых данных и разворачивается прямо в браузере с помощью нескольких строк чистого JavaScript — без каких-либо сторонних библиотек.

Это не просто забавный трюк. Это отличная демонстрация того, насколько мощными могут быть современные браузерные API в сочетании с классическими алгоритмами сжатия.


Что такое DEFLATE и почему он такой эффективный?

DEFLATE — это формат сжатия данных без потерь, который сочетает два хорошо известных метода: словарный алгоритм для устранения избыточности и схему кодирования переменной длины. Формат определён в RFC 1951 и используется в таких популярных инструментах, как gzip и ZIP.

Первая фаза DEFLATE использует вариант алгоритма LZ77: он сканирует входные данные скользящим окном, которое запоминает историю недавних байтов. Когда обнаруживается последовательность байтов, уже встречавшаяся в этом окне, алгоритм записывает пару (смещение, длина), указывающую на более раннее вхождение, вместо того чтобы записывать байты повторно.

После фазы LZ77 поток содержит смесь литеральных байтов и пар длина/расстояние. Следующий шаг — уменьшить энтропию этого потока с помощью префиксного кода. Для этой цели DEFLATE использует кодирование Хаффмана.

ℹ DEFLATE повсюду
Алгоритм DEFLATE лежит в основе форматов ZIP, gzip, PNG и даже HTTP-сжатия веб-трафика. Когда ваш браузер получает сжатую веб-страницу — скорее всего, он распаковывает именно DEFLATE-поток.

Английский текст обычно сжимается в 2,5–3 раза; исполняемые файлы — несколько меньше; графические данные, такие как растровые изображения, могут сжиматься значительно сильнее. Текстовые карты с повторяющимися символами (~ для воды, # или буквы для суши) — идеальный кандидат для этого алгоритма: паттерны крайне регулярны, и DEFLATE «выжимает» их до минимума.


Как это работает: схема проекта


graph TD
    A[ASCII-карта мира\n~несколько килобайт текста] --> B[DEFLATE-сжатие\ndeflate-raw]
    B --> C[445 байт бинарных данных]
    C --> D[Base64-кодирование\nbtoa]
    D --> E[Data URI строка\ndata:;base64,...]
    E --> F[fetch Data URI\nв браузере]
    F --> G[DecompressionStream\ndeflate-raw]
    G --> H[Response.text\nраспаковка в строку]
    H --> I[Вставка в DOM\nb.innerText]

Весь конвейер умещается в четыре шага:

  1. Исходная ASCII-карта кодируется и сжимается алгоритмом DEFLATE.
  2. Сжатые байты преобразуются в строку Base64 (кодировка для хранения двоичных данных в тексте).
  3. Эта строка вставляется прямо в JavaScript как Data URI — специальный формат data:;base64,<данные>.
  4. В браузере вызывается fetch() с этим URI, поток пропускается через DecompressionStream, и карта появляется на экране.

Ключевой трюк: fetch() с Data URI

Самое неожиданное открытие в этом проекте — возможность передавать Data URI прямо в fetch(). Большинство разработчиков привыкли использовать fetch() для загрузки ресурсов с удалённых серверов, но оказывается, API прекрасно работает и с встроенными данными.

Вот упрощённый вариант кода, который использовался в проекте:

fetch('data:;base64,1ZpLsgIxCEXnrM...==')
  .then(r => r.body.pipeThrough(
    new DecompressionStream('deflate-raw')
  ))
  .then(s => new Response(s).text())
  .then(t => document.getElementById('map').innerText = t);

Разберём каждую строку:

  • fetch('data:;base64,...') — загружаем Data URI как обычный HTTP-ресурс. Браузер возвращает объект Response с телом в виде потока байтов.
  • .pipeThrough(new DecompressionStream('deflate-raw')) — Compression Streams API позволяет сжимать и распаковывать потоки данных в форматах gzip или deflate (включая deflate-raw) прямо в браузере. Благодаря встроенному сжатию JavaScript-приложениям не нужно включать стороннюю библиотеку сжатия — размер загружаемого приложения остаётся меньше.
  • new Response(s).text() — для преобразования распакованного потока в пригодную для использования строку можно воспользоваться функцией text() нативного класса Response.
  • Наконец, полученный текст вставляется в DOM — и карта готова.
💡 deflate vs deflate-raw
Существует тонкое различие: deflate подразумевает формат zlib (с заголовком), а deflate-raw — «голый» DEFLATE-поток без оберток. В данном проекте используется именно deflate-raw, поскольку данные сжаты без zlib-заголовка — это экономит несколько дополнительных байт. Один и тот же алгоритм должен быть указан и при сжатии, и при распаковке.

Роль OpenAI Codex в создании проекта

Иво Кадзела создал этот проект в соавторстве с OpenAI Codex — современным ИИ-ассистентом для программирования. Codex — это облачный агент для разработки программного обеспечения, способный выполнять множество задач параллельно: писать функции, отвечать на вопросы о кодовой базе, исправлять ошибки и предлагать pull request-ы для ревью.

Codex работает на базе codex-1 — версии модели OpenAI o3, оптимизированной для задач разработки. Он был обучен с подкреплением на реальных задачах программирования в различных средах, чтобы генерировать код, максимально приближенный к человеческому стилю.

Это показательный пример того, как ИИ-ассистент помогает не просто «написать код», а решить нетривиальную инженерную задачу: выбрать алгоритм, применить нестандартные браузерные API и минимизировать итоговый размер до предела.

«Лучшие инструменты — те, что открывают для вас возможности, о которых вы даже не знали».


Сравнение подходов к хранению карты

ПодходРазмер данныхНужна библиотека?Работает в браузере?
Внешний SVG-файл~500 КБ+НетДа
Растровое PNG-изображение~50–200 КБНетДа
ASCII + gzip + base64 (внешний файл)~1–5 КБНетДа
ASCII + deflate-raw + Data URI~600 байтНетДа
ASCII без сжатия (текст)~5–15 КБНетДа

Как видно из таблицы, подход с deflate-raw и Data URI даёт экстремально компактный результат — и при этом не требует ни одного внешнего файла или библиотеки. Всё встроено прямо в JavaScript-код.


Почему Base64, а не просто двоичные данные?

Проверенный способ хранить двоичные данные в текстовых файлах — это, конечно, Base64. Однако эта схема способна хранить лишь шесть бит данных на текстовый символ, что даёт рост объёма на 33% (при 8-битной кодировке символов).

Этот overhead — неизбежная плата за возможность встроить двоичные данные прямо в JavaScript-строку или HTML-атрибут. На практике 33% «накладных расходов» с лихвой компенсируются самим deflate-сжатием: ведь 445 байт исходных данных превращаются в ~600 байт Base64, что всё равно несопоставимо меньше оригинального текстового файла карты.

📝 Попробуйте сами

Вы можете воспроизвести этот трюк для любых небольших текстовых данных:

  1. Сожмите текст через CompressionStream('deflate-raw') в браузере.
  2. Закодируйте результат в Base64 через btoa().
  3. Вставьте строку в fetch('data:;base64,...').
  4. Распакуйте обратно через DecompressionStream('deflate-raw').

Весь процесс — нативный JavaScript, без зависимостей!


Compression Streams API: браузерный инструмент без библиотек

Один из ключевых компонентов этого трюка — Compression Streams API (API потоков сжатия). Это нативный браузерный интерфейс, о котором многие разработчики даже не подозревают.

Compression Streams API предназначен для сжатия и распаковки потоков данных в форматах gzip или deflate (включая deflate-raw). Он доступен в современных браузерах без установки каких-либо пакетов.

Пример того, как сжать данные прямо в браузере:

// Сжатие строки в deflate-raw
async function compress(text) {
  const stream = new Blob([text]).stream()
    .pipeThrough(new CompressionStream('deflate-raw'));
  const buffer = await new Response(stream).arrayBuffer();
  // Преобразуем в Base64
  return btoa(String.fromCharCode(...new Uint8Array(buffer)));
}

// Распаковка из Base64 deflate-raw
async function decompress(base64) {
  return fetch('data:;base64,' + base64)
    .then(r => r.body.pipeThrough(
      new DecompressionStream('deflate-raw')
    ))
    .then(s => new Response(s).text());
}
⚠ Совместимость браузеров
Compression Streams API поддерживается в актуальных версиях Chrome, Firefox, Safari и Edge. Старые версии браузеров (особенно IE любой версии) не поддерживают этот API. Всегда проверяйте совместимость на Can I Use перед использованием в продакшене.

Что это говорит нам об ИИ-ассистентах в разработке?

История создания «карты в 500 байтах» примечательна не только технической стороной. Она показывает, как ИИ-инструменты вроде Codex меняют подход к решению задач.

В отличие от традиционных инструментов автодополнения кода, Codex — это совместный партнёр, способный самостоятельно браться за задачи. Система позволяет разработчикам делегировать различные обязанности: написание функций, ответы на вопросы о кодовой базе, исправление ошибок и создание pull request-ов для ревью.

В данном случае Codex помог Иво не просто написать шаблонный код — он помог найти нетривиальное решение: использовать fetch() с Data URI для распаковки, что даже опытные JavaScript-разработчики часто не рассматривают как рабочий паттерн.

Иногда самый ценный вклад ИИ — не сгенерированный код, а подсказка о существовании браузерного API, о котором вы не знали.


Итоги: что взять с собой

Этот маленький проект упаковывает в себя несколько важных уроков для любого разработчика:

  1. DEFLATE — мощный инструмент для любых текстовых и паттерн-насыщенных данных. Особенно эффективен там, где много повторений.
  2. Compression Streams API встроен в современные браузеры — вам не нужна библиотека типа pako или zlib.js для большинства задач.
  3. fetch() работает с Data URI — это открывает возможности для встраивания сжатых ресурсов прямо в JavaScript без отдельных файлов.
  4. ИИ-ассистенты помогают находить неочевидные решения — порой не генерацией кода, а указанием на существующие API и нестандартные паттерны.
  5. 445 байт — это серьёзно. Полная карта мира, видимая человеческим глазом, в объёме меньше, чем типичный аватар пользователя в мессенджере.

Следующий раз, когда вам нужно будет встроить небольшие статические данные прямо в приложение — вспомните этот трюк. Браузерные API давно стали достаточно мощными, чтобы не тянуть лишние зависимости.