← Все проекты

Кейс · Go-бэкенд

Пиксельная стена: общий холст

Совместный холст 256×256 в духе r/place: 16 цветов, кулдаун на сессию и IP, свой WebSocket-слой на stdlib (RFC 6455) с бинарным протоколом (4 бита на пиксель), снимок и дельты, снапшот плюс журнал с восстановлением, таймлапс, зум и пан, мини-карта, экспорт PNG, админ-инструменты. Пет-проект.

  • Go
  • stdlib only
  • WebSocket
  • Canvas 2D
  • Vanilla JS
  • embed
  • Binary protocol
  • Snapshot + journal
  • Docker
  • GitHub Actions

01 · Как выглядит

Скриншоты

Скриншот проекта «Пиксельная стена: общий холст» на экране 1280 пикселей
Десктоп, 1280 px
Скриншот проекта «Пиксельная стена: общий холст» на экране телефона, 390 пикселей
Телефон, 390 px

02 · О проекте

Что это и зачем

«Пиксельная стена» — совместный пиксельный холст в духе r/place: 256×256, 16 цветов, один пиксель раз в несколько секунд на сессию. Изменения видны всем сразу по WebSocket, а история сохраняется и проигрывается таймлапсом. Пет-проект.

Только stdlib Go, без зависимостей и CGO; WebSocket-слой по RFC 6455 написан самостоятельно (взят из проекта realtime-rooms), клиент на ванильном JS и canvas встроен через embed. В демо-режиме есть нарисованный арт и несколько ботов, все они честно помечены.

Ограничения

Всё в одном процессе и на одном диске, горизонтально не масштабируется. Кулдауны, сессии и rate limit живут в памяти и сбрасываются при рестарте (баны сохраняются).

Сессия это идентификатор в localStorage: кто очистит хранилище, получит новую. Защита от простых ботов, а не от целенаправленной атаки с множества IP; за общим NAT лимит по IP делится на всех.

Журнал пишется без fsync на каждый пиксель, fsync раз в 30 секунд: при потере питания можно потерять последние секунды. Клиенту нужен DecompressionStream (Chrome 103+, Safari 16.4+, Firefox 113+).

Автоматических браузерных тестов в репозитории нет, интерфейс проверялся вручную в Edge. Тесты с -race проходят только в CI, Docker-образ не собирался.

Что внутри

  • Свой WebSocket и бинарный протокол: клиент шлёт пиксель в 6 байтах, сервер рассылает дельты раз в 50 мс; новый клиент получает сжатый снимок и дальше только дельты
  • Согласованность: снимок несёт seq, дельта несёт firstSeq, при пропуске клиент переподключается, а медленный клиент с переполненной очередью отключается
  • Хранение 4 бита на пиксель (256×256 = 32 КиБ), снапшот на диск (crc32, fsync, rename) плюс журнал изменений
  • Восстановление после падения: снапшот, затем записи журнала с большим seq, недописанный хвост обрезается; битый снапшот восстанавливается из журнала
  • Кулдаун на сервере на сессию и на IP (скользящее окно), подписанные сессии, rate limit HTTP и WebSocket, проверка Origin, лимиты размеров сообщений и подключений
  • Таймлапс по журналу: старый цвет в записи позволяет откатывать историю назад без базового снимка; скорости 1×, 4×, 16×
  • Зум и пан (колесо, щипок, перетаскивание, клавиатура), мини-карта, счётчик онлайна, экспорт PNG и ?embed=1
  • Админка по токену: очистка области, бан сессии или IP, просмотр последних установок; нагрузочный тест на 300 клиентов без пропусков дельт

03 · Как запустить

Запуск одной командой

git clone <репозиторий по запросу> pixel-wall
cd pixel-wall
SEED_DEMO=1 ADMIN_TOKEN=secret go run ./cmd/server   # http://localhost:8103, админка на /?admin

04 · Код

Репозиторий приватный

Исходники лежат в приватном репозитории. Открою доступ по запросу: напишите, и пришлю приглашение.

Запросить код