Что, если бы сканируемый код не был обязан выглядеть как набор квадратов и полосок? Что, если он выглядел бы как залитый дождём ночной город — и при этом всё равно безошибочно считывался?
Это 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 проигрывает: на то, что машиночитаемый код может быть ещё и тем, на что хочется смотреть. Что ограничения помехоустойчивого кодирования и ограничения нуар-эстетики можно заставить делить одни и те же пиксели, а не воевать за них.
Закодировать текст и посмотреть, как он растворяется в тенях дождливого ночного города. Исходный код в репозитории.
Комментарии (6)

Politura
07.06.2026 13:25У большинства точек контраст по отношению к фону слишком маленький. QR работает где угодно, на слабом освещении и смазанный, этот только когда света много и картинка не шевелится. Если попытаться засунуть его туда, где используются обычные штрихкоды, или QR, пользователи будут злиться и ругаться, и вся очередь за ними, когда каждый второй не может распознать и должен вбивать цифры и буквы руками.
sanchas
Как-то раз я экспериментировал с QR-кодами. Я взял QR-код и нарисовал поверх него второй QR-код, но не просто перекрасил квадраты, а нарисовал внутри квадратов кружки занимающие по площади примерно 25% от площади квадрата. Получилось два QR-кода в одном. Если сканировать его с близкого расстояния, то он читается по кружкам, а если с большого то по квадратам. Но наигравшись не нашел как применить эту идею...
Staster
Интересно было бы посмотреть, можете прислать пример такого куар кода. По поводу NoiR Code прикольная идея, но все портит необходимость в стороннем ПО, вся универсальность куаров в том что программы для его чтения есть везде.
sanchas
Старые исходники к сожалению пролюбил, поэтому на скорую руку написал заново. Примерно так. Тут квадратиками зашифровано "https://google.com", а кружочками "https://github.com". При некоторой сноровке и игре с масштабом можно поймать оба варианта.
AlexKniga
Интересно. При расстоянии до qr более ≈1.5 метра "https://google.com", ближе "https://github.com".
sanchas
На самом деле всё довольно просто. При распознавании учитывается середина квадратика. И если QR-код большой и виден четко, сканер срабатывает на кружочки. А если изображение нечеткое или маленькое, то берется усреднённый естественным образом цвет, который ближе к цвету квадратика.