Что, если бы сканируемый код не был обязан выглядеть как набор квадратов и полосок? Что, если он выглядел бы как залитый дождём ночной город — и при этом всё равно безошибочно считывался?
Это NoiR Code. Он кодирует текст в изображение, которое читается как чёрно-белый нуар-кадр: полутоновый силуэт города, луна, штрихованные тени. Наводишь камеру в специальном приложении или веб-сервисе, созданном для поддержки этого формата, и получаешь своё сообщение обратно. Попробовать можно здесь: noir-code.suncake.xyz

Картинка выше — это не изображение, в котором спрятан код. Картинка и есть код. Точнее, данные несут центры ячеек сетки; город, луна и тени полезной нагрузки не содержат — это искусство, выросшее поверх той же сетки.
Почему
Всё началось со звучания. Я часто слышу, как «QR» произносят как «куар» — а это почти «нуар». Одна буква, и сухая аббревиатура из мира логистики превращается в название жанра про дождь, тени и неоновые вывески. Идея напросилась сама: а что если сделать QR-подобный код, который буквально выглядит как нуар?
Зачем
Мне было интересно. Часто бывает, что я чрезмерно увлекаюсь какими-то занятиями и идеями. Это происходит внезапно, и так же внезапно я теряю интерес к этим вещам. Видеоигры, книги, разработка, мысли. Сейчас благодаря нейросетям я могу реализовать эти проекты раньше, чем потеряю к ним интерес.
Как он остаётся и искусством, и сканируемым
QR‑код тратит всю свою площадь на машиночитаемые квадраты. NoiR Code делит каждую ячейку на две части:
Центр каждой ячейки несёт одно тоновое значение — чёрный, тёмно‑серый, светло‑серый или белый. Только это и считывает декодер. Маленький, чёткий, устойчивый.
Кольцо вокруг центра — пространство для творчества. Штриховка, перекрёстная штриховка, кромки зданий, градиент неба — ничто из этого не несёт данных.
Декодер видит чистую сетку тоновых точек, а глаз — низкодетальную гравюру города. Рисунок генерируется процедурно из самого сообщения: каждый текст получает свой силуэт города и положение луны, детерминированно, — так что одно и то же сообщение всегда даёт одну и ту же панель.
Стиль можно усилить, включив штриховку данных. Так даже центры ячеек рисуются штрихами и вся панель читается как ксилография и она всё равно декодируется, потому что средний тон каждого участка точно попадает в нужное значение. Тем не менее, стоит отметить, что такое оформление усложняет декодирование в реальных условиях.

Выживание в реальном мире
Код полезен, только если его легко отсканировать с телефона. NoiR Code для стабильности сканирования использует три вещи:
Чёрная рамка по периметру — единственный опорный элемент. Декодер находит это кольцо, считает гомографию и распрямляет панель обратно в квадратную сетку так, что наклонный снимок под углом всё равно выправляется.
Коды Рида‑Соломона означают, что часть панели может быть смазана, засвечена бликом или порвана, а сообщение всё равно восстановится. Дополнительная тоновая полоса снизу дублирует байты заголовка, поэтому восстанавливается даже стёртый верхний край.
Тоновая нормализация растягивает контраст фотографии перед считыванием, ведь камеры телефонов поднимают чёрное и заваливают белое. Отдельный проход подгоняет плоскость освещённости по заведомо белому полю‑отступу и делит на неё, убирая неравномерную засветку, какая бывает, когда держишь панель под настольной лампой.
В итоге считываются и чистые рендеры, и скриншоты с оконными рамками вокруг, и фото с монитора, и снимки с рук при неровном свете.
Сетка растёт вместе с сообщением
Короткому тексту не нужен билборд. NoiR Code выбирает наименьшую подходящую сетку — от 16×16 до 40×40 — так что короткий URL умещается в компактную панель 578px, а абзац разрастается на больший холст. Какой размер перед ним, декодер выясняет перебором кандидатов, а контрольная сумма подтверждает победителя. Никаких маркеров версии, никакого лишнего обвеса.
Сетка | Ёмкость | Панель |
16×16 | ~22 байта | 578px |
24×24 | ~58 байт | 770px |
32×32 | ~109 байт | 962px |
40×40 | ~173 байта | 1154px |
Работает в браузере
Веб‑приложение целиком делает кодирование и декодирование через небольшой публичный API. Вводишь сообщение — получаешь панель. Или переключаешься в декодер: загружаешь изображение либо жмёшь Сканировать и используешь камеру — она снимает кадры несколько раз в секунду и останавливается в тот миг, когда получает чистое считывание.
Аккуратный небольшой стек завёрнут в докер, как горькая сигарета:
Ядро на Python делает всю настоящую работу — конвейер кодирования/декодирования, Рида-Соломона, машинное зрение на OpenCV — и выставлено как внутренний сервис на FastAPI.
Шлюз на Go стоит впереди как публичный API без состояния и проксирует запросы к Python-сайдкару.
SPA на React — это интерфейс, с переключателем EN/RU и нуар-темой: очень тёмной, с острыми углами и красными акцентами.
Всё развёрнуто на небольшом кластере k3s за Traefik с автоматическим TLS, собирается и публикуется по CI на каждый мёрдж.
Исходный код можно посмотреть в репозитории на Гитхабе.
Сравнение с QR
И давайте честно: для любой практической задачи QR лучше:
Ёмкость. NoiR Code вмещает максимум ~173 байта. QR — до нескольких килобайт. Короткая ссылка влезет, абзац текста — уже нет.
Размер. Ради этих 173 байт панель раздувается до 1154px. QR той же ёмкости заметно компактнее и плотнее.
Совместимость. Ни одна камера и ни один телефон не прочитают NoiR Code из коробки — нужен специально созданный веб-сервис или отдельное приложение. QR читает буквально всё. Это перечёркивает большую часть практического смысла.
Устойчивость. QR бинарный — чёрное или белое, его трудно сломать. NoiR использует чёрный, белый и две градации серого, поэтому он чувствительнее к освещению, JPEG-сжатию и дешёвой чёрно-белой печати или копиру.
Цена декодирования. Чтобы прочитать панель, нужны OpenCV, гомография, перебор версий, поворотов и рамок плюс коррекция Рида-Соломона. QR декодируется мгновенно и где угодно.
Стандарт. QR — это ISO и огромная экосистема. NoiR Code — самоделка без всякой совместимости. И стиль ровно один — нуар, на любителя.
Так что нет, это не замена QR и не пытается ею быть. QR-коды победили тем, что они чисто функциональны, — и выглядят соответственно. NoiR Code — это ставка на ту единственную ось, где QR проигрывает: на то, что машиночитаемый код может быть ещё и тем, на что хочется смотреть. Что ограничения помехоустойчивого кодирования и ограничения нуар-эстетики можно заставить делить одни и те же пиксели, а не воевать за них.
Закодировать текст и посмотреть, как он растворяется в тенях дождливого ночного города. Исходный код в репозитории.