Уместить звездное небо на WebGL в 1009 байт JavaScript

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

    JS1k — ежегодное соревнование, где надо вместить демо, игру или все что угодно, в 1024 символа на JavaScript. В этом году мое демо заняло четвертое место (до третьего не хватило каких-то два балла). Посмотреть демо можно на сайте JS1k. У кого не открывается или не работает, выглядеть должно так:



    Минифицированный и полный исходный код лежит на github. А под катом разбор того, как сейчас минифицируют JavaScript для таких конкурсов.


    Дискалаймер


    Основная красота демо — фрагментный шейдер авторства Pablo Roman Andrioli. Pablo — художник, который работает с фракталами, и на форуме fractalforums рассказывает некоторые подробности вычислений. Моей же задачей было упаковать шейдер и WebGL код в 1024 байта.


    Инициализация WebGL


    Обертка JS1k при старте демо предоставляет WebGL контекст в глобальной переменной g. Несмотря на это, работа с WebGL очень многословна. Например, чтобы добавить вершинный шейдер к программе необходимо 159 символов:


    // Define a new program
    p=g.createProgram();
    // Basic vertex shader
    s=g.createShader(VERTEX_SHADER);
    g.shaderSource(s,"attribute vec2 p;void main(){gl_Position=vec4(p,0,1);}");
    // Compile and attach it to the program
    g.compileShader(s);
    g.attachShader(p,s);

    Для решения этой проблемы во всех решениях JS1k последних нескольких лет применяется трюк с синонимами функций:


    for(i in g){
      g[i[0] + i[6]] = g[i];
    }

    Цикл добавляет для каждой функции (и для любого члена) WebGL контекста синоним, который состоит из первой и 7 буквы. Например createProgram становиться cP, shaderSource — sS, и т.д. Дополнительно обрамляя весь код конструкцией with(g) (которую использовать в настоящих проектах нельзя), получаем:


    with(g){
      p=cP();
      sS(s=cS(35633),'attribute vec2 p;void main(){gl_Position=vec4(p,1,1);}');
      ce(s);
      aS(p,s);
    }

    Минификация шейдера


    Оригинальный шейдер занимает 1100 символов. Основные сокращения: удаление лишних переменных, и объединение похожих фрагментов. После всего я пропустил код через онлайн минификатор. В результате от шейдера осталось чуть больше 500 байт.


    JSCrush


    JSCrush — де факто стандарт для сжатия кода в таких соревнования. Утилита превращает код, в приблизительно такую последовательность:


    _='(i a.style=...
    _='(i  a.style="widMj%;hEjvh;:left",g)g[i[0]+i[6]]=g[i];wiMO.u=g.G1f,x=y=k=g)p=cP(35633"tribute 2 p    gl_Posit=4?FN"precis mediump ;G Zt,a,x,y   Uf`ord.rg/64!-.f.=a;Zc=+xz,v=+yz;m2 m$cc-cc)s$vv-vv)fJf#Ur`Q,,r+`t*2.,t,-2.rJr#Zg=.1,b=Q;Ui`!Kl=Rl<2Rl++){Uo=r+f*;oQ)-mod(o,2.))Ze,n=e=!;Kd=Rd<2Rd++)oo)/dot(o,o)-3,n+o)-ee=oif(l>6)Q-max(!,.3-i+=b+g,g,g)*n*5*b;.73;g+=.1;}i=mix(i)i,.85lor=4(i*.01.lo?ug?bfO=34962,cB()eV(0vA(2,5120bDO,Tw Int8Array([|,|]35044o=,(Lt@-oa@TrHE/TrWidMx@xy@ydr(6,3requestAnimFrame(L)})(down=upk^=1},movek&&(xX,yY)};),3=funct(e){uOf?,"flo}@ce(saS?,slengM(onmouse    ;void ma(){Tw De/1e5);incos(for=abs(gl_FragCo,1g*(sS(s=cS(n*n*.001at.5vecionb*=s(=e.page0,!0.#.r=s;$=m2(?(p@"EeightGunimJ.rm;K(t MthO(gQ1.R0;TneU Z `=j:100z/50!|-3';for(Y in $='|zj`ZUTRQOMKJGE@?$#!  ')with(_.split($[Y]))_=join(pop());eval(_)

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


    1. Находим символ, который не используется в коде
    2. Находим повторяющиеся фрагменты в коде, которые заменяем на символ из первого пункта
    3. Заменяем строку на символ
    4. Повторяем 1-3, пока результат меньше чем исходный код

    Оптимизация


    После всех операций у меня в запасе оставалось еще порядка 30 символов, которые можно было использовать для оптимизации производительности. При загрузки демки, можно указать размер canvas’а: на весь экран или фиксированного размера. Запускать на весь экран — красиво, но фрагментный шейдер вызывается для каждого пикселя и работает медленно. Выходом стало — запросить у JS1k canvas’а фиксированного размера (я выбрал 640х640), а потом в коде его увеличить до полноэкранного:


    a.style='width:100%;height:100vh;float:left';

    Тогда изображение занимает весь экран, но шейдер выполняется только на каждый пиксель оригинального размера canvas.

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 23

      +7
      То есть вы взяли чужой шейдер, применили к нему несколько известных приёмов и назвали это «моё демо»? Есть, чем гордиться:-/
        +5
        Просто делюсь. Перед тем как делать демо, я посмотрел список из почти 100 участников и удивился, что никто не додумался минифицировать красивый шейдер. На самом деле, там и WebGL участников не так много.
          –1
          Действительно, тупицы какие, никто не додумался взять чужую изначально маленькую работу и её уменьшить.
          +1
          Айвазовский берет холст, произведенный не им, краски, сделанные тоже не им, применил к ним несколько общеизвестных приемов.
          Перельман взял общеизвестные математические символы, применил к ним несколько общеизвестных приемов.
          Хм, так же скажете не чем гордится или это не их достижения, т.к. предпосылки и материалы не они изобрели-сделали?
            +11
            В данном случае автор скорее сделал рамку к картине Айвазовского.
              0

              Это у Перельмана-то общеизвестные…

              +1

              Омг, серьезно? Он что, продавал это за деньги, воруя чьи-то наработки или что? Это все на энтузиазме и просто заставляет подумать как достичь впечатляющего результата, исходя из практического ничего. Вот из-за таких умников как вы, в России жопа и пи*дец, так как никто никого не любит, не мотивирует и не видит в ближнем своем друга, а видит только возможность докапаться и наехать.

                0
                Нет, он всего лишь занял на каком-то там конкурсе какое-то там место. И ещё похвалился этим тут. Причем заслуга тут, в основном, автора оригинального шейдера. Был бы тут разбор, всё было ок, но ТС, похоже, особо не вникал.
                  +1

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

                    0
                    Нет, по моей логике канвас использовать можно. Если бы ТС сам рисовал на канвасе такую графику — честь была бы ему и хвала.
                      0

                      Все-таки нет, все-таки по вашей логике нельзя себе приписывать какое-то творение, если оно не создано полностью собственноручно. По-вашему, никакой фреймворк не имеет прав на существование — ведь он всего-лишь оболочка для каких-нибудь DOM API итд. А если сравнивать пропорционально — сколько умественных усилий потрачено, например, на написание "сжатой галактики" и, например, того же Реакта — то тут вполне сравнимая аналогия — логика Реакта тоже несравнимо мала относительно логики тех же браузерных движков и их API. Ну так тогда давайте скажем все дружно — Дэн, какого ты приписываешь себе Реакт, это ж не Реакт вовсе — это просто обертка над API. Если вы думаете, что API это не тот же шейдер, то вынуждет вас поправить: никаких различий между ними нет. Это third-party код, что там, что тут. Просто в API это все завернуто в интерфейс. А тут — просто имплементация без интерфейса.

                        +1

                        Не надо приписывать мне какую-то левую логику и передёргивать.

                  +1
                  Помню был у меня случай, когда я видел в ближнем своем друга и товарища по разработке. В результате сделал ему диплом, порекомендовал его на рабочее место и теперь он начальник с моим проектом в портфолио, кучей людей в округ себя, а я сам себе «режисер» и неадекват «с которым не кто не хочет работать». С другой стороны производственный процесс не позволит вам заниматься самодеятельностью, а продвигаться лишь при помощи заимствований чужого труда и открытий. Так что, тут либо человек карьерист, либо творческая личность. А статья… небольшой твик как сделать заготовку для мини демки.

                  Печально другое что автор статьи не раскрыл алгоритм рисования такого, а это несколько слоев своеобразного фрактала с MIT лицензией.
                    0

                    Конечно, может быть ситуация, когда на тебе едут всю жизнь или не благодорят за помощь. Но тут немного другая история, на мой взгляд. Корень моего возмущения — это то, что в такой плевой вещи (подумаешь, конкурс на маленький js сниппет) — люди и то видят какой-то подвох и ищут проблемное место вместо простого "спасибо, интересная статья" или хотя бы просто "очень красиво получилось".


                    Да, безусловно, автор не написал этот шейдер, и да, безусловно, этот шейдер тут по сути и делает вау-эффект. Но автор не выехал на этом на крутую работу и не забыл упомянуть откуда он его, в конце концов, взял. Просто любой из нас — натура творческая, в той или иной степени. И все вот эти конкурсы и маленькие победы — они с целью нашей же мотивации развиваться дальше. А это надо поощерять в людях, пусть даже в этом конкретном примере не все, что сделано — сделано автором.

                      +1
                      Я как автор подобного и участник множества других конкурсов прекрасно понимаю как у нас любят наезжать с возгласами — «посредственность». И все таки при виде таких статей осадок остается. От других не потребуешь все то что требуешь от себя. И тут задумываешься — а стоит ли париться.
                      +3
                      Я в статье и в сабмишине написал, что шейдер готовый, я только минифицировал код, и прикреплял ссылку на оригинал. Я в заголовке сабмишина «Andrioli Galaxy tour» указал фамилию автора фрактала. В статье, я дополнительно скинул ссылку на фрактал форум, с описанием этой серии фракталов. С моей точки зрения, я нигде не присваивал себе чужой труд.
                        0
                        Да я пропустил этот абзац. Смысл только в том что с MIT лицензией можно пользоваться данным «продуктом» достаточно свободно. Поэтому к вам тут претензий ни каких и быть не должно.
                  +4
                  Оригинальный шейдер занимает 1100 символов.

                  А даёт очень красивый результат. Это куда интереснее.

                    +1
                    • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Надо сделать всё это в equirectangular projection и будет уже авторская работа.
                          0
                          Выглядит потрясающе, а то что вы уложили все это в 1024 байта это очень круто. Пощелкал по другим демкам, очень интересная со змейкой и шахматами. Хотя змейку лучше наверное писать на питоне.
                            0
                            Как нарисовать сову.jpg

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое