Sergey Horse
RUEN
Проекты

Готов · июль 2026 г.

Aurora

Браузерная песочница для написания GLSL в реальном времени

  • webgl
  • glsl
  • react
417
Тесты
8
Пресеты

Идея

Aurora — браузерная песочница для GLSL: пишешь фрагментный шейдер в одной панели, смотришь, как он компилируется и рисуется в другой, и делишься всем сразу — кодом и значениями всех ручек — через сжатую ссылку. Ни бэкенда, ни аккаунта за этим нет.

Интересная задача

Запустить шейдер — простая часть. Сложная — внятно сказать автору, что сломалось. Песочница оборачивает пользовательский код в служебную преамбулу (строка precision и объявления uniform), и это сдвигает каждый номер строки, который сообщает драйвер, на фиксированную величину. Драйверы к тому же не сходятся в формате: ANGLE пишет ERROR: 0:12:, Mesa — 0:12(5): с колонкой. Aurora разбирает обе формы, вычитает смещение преамбулы и ставит маркер на ту строку, которую назвал компилятор. Ошибка, попавшая внутрь самой преамбулы, прижимается к первой строке, а не пропадает: маркер не на месте всё равно лучше, чем его отсутствие.

Редактор Aurora: ошибка компиляции подчёркнута на 15-й строке, превью продолжает рисовать, снизу — ручки uniform-ов

Пропущенная точка с запятой на 14-й строке, маркер — на 15-й, там, куда указывает компилятор. Превью держит последний скомпилировавшийся кадр вместо чёрного экрана, а объявленные шейдером ручки остаются живыми под ним.

Один контекст на всю стену

Каждая плитка в галерее пресетов — работающий шейдер, а не картинка. Очевидная реализация даёт каждой плитке свой канвас и разваливается: браузер выдаёт примерно шестнадцать живых WebGL-контекстов. Поэтому у галереи один offscreen-рендерер: он по очереди рисует туда каждый пресет, а ячейка берёт дешёвый 2D-контекст и получает готовый кадр через drawImage. Ячейки ничего не стоят в бюджете контекстов. Браузерный тест закрепляет инвариант: считает настоящие вызовы getContext, использует контекст превью как положительный контроль и требует, чтобы стена добавила ровно один.

Галерея пресетов Aurora: восемь анимированных шейдерных плиток сеткой поверх редактора

Восемь пресетов, все анимируются одновременно, все рисует один рендерер. Панель сама об этом говорит в подзаголовке: живые превью на одном общем холсте.

Что вышло

Два режима над одной моделью: Config, где объявления uniform-ов превращаются в живые контролы, и Code, где дело берёт на себя редактор. Контролы написаны руками, а не взяты нативные, поэтому слайдер диктует ровно то значение, которое человек видит на экране, а не шаг, который врёт. Шеринг упаковывает исходник и значения всех ручек в ссылку, и, поскольку эта ссылка возвращается обратно как недоверенный ввод, покорёженная грузит дефолт вместо краша. Приложение записывает и само себя: PNG-кадр, WebM-клип и анимированный GIF, который кодируется в воркере. Снизу лежит неприметное — восстановление после потери WebGL-контекста, ограничитель кадров и статичный кадр, если посетитель просит меньше движения. 417 юнит-тестов в 37 файлах плюс 26 браузерных сценариев против продовой сборки.

Чему я научился

Восстановление WebGL-контекста научило тому, что событие браузера бывает вопросом, а не уведомлением. Когда контекст потерян, событию webglcontextlost нужно отменить действие по умолчанию через preventDefault — иначе браузер никогда не выстрелит парным restored. Пропусти одну эту строку, и весь код восстановления ниже становится недостижим: молча и навсегда, причём любой тест, который мокает событие, продолжает быть зелёным. Всплывает это, только если отобрать настоящий контекст у настоящего браузера и дождаться, вернётся ли он.