Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <h1>ENTROPX - a pretty limited drawing language</h1>
<p>Author: Mario Klingemann - @Quasimondo<br/><br/>
ENTROPX is a minimalistic language designed to create pixel and glitch art. Letters of the alphabet move the brush by a certain x/y offset, their distance and direction can be looked up in the grid below.</p>

<div id="holder">
  <canvas id="output" width="512" height="512">
</div>
<div>Presets:
  <select onchange="document.getElementById('code').value =renderer.lzw_decode(this.value.substr(1));draw(this.value)" id="presets">
    <option value="$#mnmsā3meessts3t4ot17orĐ3rnęneĆbćmmęġkonooGGoa6āwĄĄvĜsĝmann5Ħ5ćsUu3ħħĦĨńeĕsŅŊoŌŊĊDołęwaodr6na4ĠŊgĨQq1ĆVvĨg11ĦŪķŝe6ċĥebċ3ċsĥŊtđĻroIŏŌtĆeķłĽĊĐsůķāĦrƎaŷōĄjaġƓm">Phone</option>
    <option value="$7ses3e6ot3ăeme13muou4mam3a3nr5onaağ6ěęě3opoc18ćĉe21mLLxmmsxossgeqĠĉľĻăĞnĆļħ4nxrnnmveaœrŔiŔhrrueehuśĻrtnoţţħāšĶsĕa">Phone 2</option>
    <option value="$#onnanrrornāoosme4oaāmēotss3o3Ęr3ăĄaĠğġmmsĘ3e7ĕĠaaħĩ5ĩĨČĎsaseĻ3ď4ĘtĆmj">Dog</option>
    <option value="$#nonntā10nx6sus3H4nmshmneăwsosm3aćmLdąooknub3ĂQbēsbnr3ĝm5sChċaxt3ĉsěĎowi4oīĂĜĂr5nwnaĄt4sdspěĵo6nLlāŢŋţa7ełcř8Ĝ9ĞnpŜĄpŌcēa3ĞstĿożžĜŻĵumeeķĝŃrłĂijŖĬŀİmđwĖFkžxaexŖĴĄăĦro4rijƦrƟčĝăgłgŀtŏĖmaaƓĒơƭĂaĝŏrňŇscƺƍVJHĒēbĝŖ7ĜēQŖƽƉg6oĴŋƦo8ƹƹwoFf8ēodŹŽ3tŻċčŨbimēŮłŸƬIJƵndmŞŋǝm7sjėnvȁjđœǠnfDzƆłƴĝƆtĦŲāśŻĸĿŗǞǸsLDžuegǧěăvċmjmDZǍěƩhƆőŒƴƷŸŒŨǫkȔȽĜňŀƽ4ȷƱăƦGgŝāaŏĦNJnjtKƮcƣƎĂȔĒĴēĖ31ŷŨo">Cyclist</option>
    <option value="$64mLLlmmeesstorFntĊećČ6Iiuāe46ċ62č33n6hĤiBm8namĬĊČ12scnisinmx21smĿeoēnnčĩĊĮĺʼnŅŀĿōŌŋŏŒŊŎŕőcoumķœŖŐŞŘgŚśŝŗšœfĆpőşŔŅřoUĆŦŠŧĮďŴpŠ3ĮooĺcŴĆsosŽĺrtnhbĿģp36nrrʼnĢs55oļ1nų7n42m7s4GčƔ10ođ11ŜuIJĞohāa5Ɣ3ƶſkƆĆq4Ʈ5seŽiƛr1ģƕƮđǐċČƪƕnuimaǍſđGƩ1ĝƣmǠnwnqŎfoĝĆarƶmŻēiſdžŜƪoǴhr9ƇŊſhćƐǷǘǣ4ņǰLJȈƋȀĆhǰȏȁm4ȅƬĤoƬsFHHhĊǃŅǹƀČĐƛĐȣ19ƀņljoȇƄƅeuņǮŽsĭȷŀǓȲĺŀȸwčgr5ŀńrȶƅDȮȳƼȁsuɇȺƈɑċŀȳoȵɒĮɔĭćnCnƅņs7ƀƆưɦsȩȫŀĮɭŋɮmtǙŏɯɶɵŏȶɮɸɻɽnɺȽɳĭʂɈŷŶʇnĉǿJngƚoV3ȝ8Ǚ27Ŋʖč8Ʈs9mD3ƩDGg6ľe70Ǚ7ơǯʐVae8Ʀtt7ǃƕʳĬaƔʸIJġčˁǕaĭ˅ǙǙʼǎʮɇƮǃtƼˏƮſǯrˆ˅ˌņȏȸĮɄ˄ȖkǽgȴɣƆƀadƇġmw6ƆłxɲĆɰĤŀƛx˭ƕ1ƺQėƦuģțŅǮƳʪeah1ʸr̂ǠćİěsB5ųdžĒ̖̕ĉĈĦ̋̋ƔŅ̝̋k̠̟ƓĦpaIJ6mjIJǪ̈̄̂2̪Ĉ4ċeĝđċİ̺sˢĬ̂̿̾ƔNj2Ġʵ̗ĉdžBUUudž3͎sŻŽʼǃƷȾɵmlɄċƆƧ̫jǵfŅʊĺjj͏tpćƺĚōŬŏvƼōʼƛsboʤ˥nkģ̪tDgŽeĬĮʼĐȪķasII˩ȈĊǼƶnjǙn˺ƪnʗěΔ˭ͽŎǚĿȇưɭ͏jŅ4ʔ͏ΧˮĆɳĮ;˲ŽbmkʔxoK͝ĭĩōŋaāɵ͏ĉΊɮ͔ƶοɻξĻǃͶɵΆDZ΍ȊŀpłĿɱƷɬʹƀrǭmģξsJ̈ʪ͏Ƭǹȷŏʗ͌āh͵ſΆ΃˴̴ʗ̴NjǃŬĮIJϝsNj1ˮɢͼ̞Ώ͵ēWɲ˥͝ȳāiǃ͏rϋˢʸȽcƼ͏ΊĉАoƂǙģxЌϧŎИˏȽυ̋ΔЃϧЗƢƶƼϕ̴ĮŽ˨ājŀ͝Čȟ̫дİƼrřкΗſɿŜŽгkСģǭЪķȮȈсϙ̼nЃLjοǓɢtȇǜȺſЕοQsj̋͛жΚċл̸̞tűhƼxƼƂϙǥЬpͷѬƽρ͓ōŎLJΔȽĒūu5ʔѪmѪƮģāŬĦhɥŬxˑ̝ȣоҏеѥΏѤпғġĺiуLjҜт̫ҝ̫̝ѣČpā̟ģʞ͏QQ΃ʼΆΆ͍̹̹ǐtƖʛ˨ҵ˥Ħ͝һɣƅƅҼĊҾҽӁҿӄӃӆӅƶӇӊӉӌӋӂӀӐӏӒӈӓӖӎӘӍӔӘ˥ƅǯƅţcӓӡoXϻ̝ʱōń΃śǶŐȳӮmϡg1ɥgJHŅѧлĵoͨƛͱmӻLdǵLƩɪʔѿ΍ƛtșҦmVhʞξƔ˭ӻſL3ĹhгЮŅĦǥğćƴ6ʛXėː˺s">Apple Mac</option>
    <option value="$:f@x6ma9o@IhrmmsĎuąunra7oP3mtmjoĈxvnn3oscđu3nčtėĤmui4ĈIĥĬioUuUmwačsğďm@KsĴaxĚtğpŁĦěĥanoLoĔkĚhčŕgğrĚrCC4mĻĚaĴGgĴ">Mario</option>
    <option value="$#ossmnmĂlmaĄneănČaraeđunongĘmu3ęěĖn6oisĘh8āĘ5o8stĆıse4ėsrjČnHėąuĎĿĄwĮoohĶi4ĦĵcĶpŊoĵo5s5mĵŗăĵavąs">House</option>
    <option value="$#9miIsi12op11ĂixĂ6oVu13oXi4soe2Č30noonĢi2Ė25smsGgoĠn26ĭs24ı0s5mrhmmJhc1ĵh15ĒjĖŅĵHnʼno">Notes</option>
    <option value="$#amaāăĂĄanrna3es8tUmsmnċĔĖ5Ĉ4Ċnmmr3ntssx5nh4Ğ3seīm9Ĕ9Ğ10ĔĶĞIJmĢĪĺĔĭopnfoGo4em12soĹt5siĥo8ģ7Ŏ6ģŒňnňŎĖoĥ3oşšţottrr4ŪġŦģšĉĭ5o7np4sjėstēmİāĂğƅƈƇƆĈşċşĢţŎĮƑőŪ7Ū6ŪoIsĉşŞŮĉĨ5Ă6ƥƧżjmt6ĔśāeĞřfģİƮŀŽľhĵķƔŔtƚǀǂǁǁĔ">Intro</option>
    <option="$#ogotonāorng25ntČsmssaēmneėmęuĚęněmcĒġģđğĆHn8ą7mhoowāsąĆĵĆnaĒĈĠĺĖĠrėeĺaĆoĻeēoĵsQm3ĔnĘŋĵr3ĎspsińĤĠđn5m17īćĸąĚđu9Ť9s5oPĵĂtğġĠĜmkĈmĝđ26ĹrŗŌ3ěűoŘg3Ō4ĒƊƌƎsƐēŲƓƏƑƘĊƔƖƒnDŌūĵ22sUmėo18n11Ē10Ƭ6sŊūƹn">Help</option>
    <option value="$4so7nrā21spmkor23nmnooāsmt22ėėeemmae12ġa18ġnĄĘt33o1ĐmeĀe3sķ4ġ4ĄĽrĿĐŀoēāntŅńĮoWm4ĕńŐŇʼnŔŒŒ11ĕuĩ">File Index</option>
    <option value="$#nooqss3oāannttoąĎeaaĆ3mLDgb14sGgĝamme3nusmnmsPsoeĂn8Ĉćjmarogĩt5s7o4āteiĂĹtĦmkorĦaća9āİ3itįŋrīŜmŜćċ4Ĉīeue8m16seģĦoa12nk4Ū7sVneŅĩĪĹruģāċĤĤĩ7ngobsiĺŜxŃĪo5n6ăģBƇeĩŰkmunKg5Ƣ7m21sŷŃćWŪ1ncĂHnĖĝPIIiŪ0ƘĪwĖĦƙQsĴCƅĎČŤĪĤīīĬžƄrƈĪeŃĺďsŜ">Book Shelf</option>
    <option value="$#9stoonąnenĆnm3ngse3oĎxo5naĊosĂĝĜmĝt16sfĤČěsoĤspđġĒ1ma18nrąt7otĝağćhĩ8mk12Ĝ3mes4Ľor4īĘwĉĆrrĸn">Address</option>
    <option value="$16sm15n3m9Ănmno7ċāaąm3Ă3ċsĊĘseėm4Ă4ċ7sctoočsĨPĊmmsiĕČnuīsu1Ģă5ķľČaċĻnnt11ċĉĈċe18st7ĩt6ĩoīĢoĠo6ċĆİįŞrtsr3ocĕo12č1ijāŧnjęğŅtŮŝņŏİLLGo3ňŁŅī3ijĜ34maa3ŭrrŖīĪƓƖƕƘŗƗƚƙƔƜŗrsŕn5m">Slide Show</option>
    <option value="$7unn14o16se4mhoor5nammFn3mIsċJnĚe5st5oiĘsĢcgĉĉemĒBmotsīeĴĮaahnmsixľaĨm3Ē4sŅĽńĽrŋŊonlsoņdĒanrijr3őŎĘa1ņ">Bazinga</option>
    <option value="$#sesQi3stssdtwmĂćĉĂeĆtLs3oręrnnr6naġ3ġĚotħħĨdm5ĈxnHHĞoVJijmQpsjĝĢrĤĜłęięġodeĨĜaoxrĚđĎmœďďĈĈĐŚŖŗĆeĖœĉjmaŔťťĂĈeœJhn3ĚĚtĨħ6ěōřeŊoohosſā5œmāāmbrdėtmkżt4ħƇƅťsĢ3mve4ŕĆFƐtĘaruƎogƆſocĉņĨntĜųėūƶŔēmžočsąmƥĜƛsxť3ĢĢġĝłQmľůĢ4LjLjNJƘŧƙƷ3ĒƷūǎŐĞNjn6űűęǦŀǦijǠa4nxoľŎrŏŇŏőƔƆŘŞIitVvĚ">Rex</option>
    <option value="$#orrngtānnamesuaĊm6sIs11mHnoFoćĉċssVwĒmmLġp8sCHH3Ěn13oaot3stĈosoĚľŀĿo9ĢVďxttĺĆġ3mĊ3awŊijsrġĦsŐř8nmİhhXĔsJĉİpmswrāŏsemnrċūsahŠśġŊĝ4Ěgo4ĺdmwŲaĨŁŋŘĝeĽŰūĽƎnĴĆăĚĽŃŗşşųŴŁnĻDoĖs27m29ƚƖŠ1ƨ9Ʀ5npƵx1Ƶ">TO DO</option>
    <option value="$3i9oxsp9mWuseanončotĐčdnmnrnbęscěĎĐnĖĘđosjn18ď16mUmsĘIJĘ4IJ3oIs10mHn6ďįeėijee26sttohĎchĬœmvĎsď3hjĘoŒĽoāmpsxĂţāĺĻmiŢćťŭ15ďŐŐoħeřċŻīďor28ėņř27sU3ŞovĽmhšhcďmjhćhŴŷħŭĹŷřnƎľhƑ0ům">Weekly</option>
    <option value="$#uiighgăQăFFouVmhuHHn8oĔmenmsmĚĜĞĝěměsosiħsunĖaĬnĮjĖģğĢĠķĶĹĵĜee7sGoGŁĄĆĄh3āQmBUm6sDŁŁ3ŇāuĐiQŎŎĿŒG4ŖiŘŊmuŎ3stt17ĖhnŰŰĭűŵrĥseŸźĦĨżŹŻŸ1ĕĬr28ĚĞĥ2ĿVačjĂŤţhągFňŘąăgLog">Calendar</option>
    <option value="$#asmne4sosunUmĉno3nwnčăPsxĎonhn4m9s3ěnrćss4oaďntĨģnĭįeģrijİĤěĴĪIJĐĥĜoěmmeĨčBčĊĿkĔĂărķbĭĬdĔ4nađĈħcŎgďćšěĈĦěģŏĨačČĔčįĈwst3ųńspŢőGěcJľĮsņiħeņťĮšQsŮųpďĈņWuĨ8o2ŘƗ5s10ăe9ăč13mā5m25nŨńƤĢorđŕaīŕĔřśćŃpĈżřƉŜĺŸƄjĂųįūųŵƇŤŢtĥđŒĭķāeĜjšsŖăţǞǂľĈmpŲįčkƂŵljůbǐgłĢmĆĈĊ">HyperCalc</option>
    <option value="$16on15menmsmĈĊČċĉďčĐĎĔēmĉoiosoĂ28ěĝnoteeĘkssCă8ĊĪěeuĜdģo25Ĉm3ģstnģğses42ċģĿw26ģģ3ĊdńordčbĊĄĜĪĉKĂěbetşěğşkanćVjģ7naĊĦgopěpũċrmpĆsũŎĢėŪŀĻſŐaŐĝĜbĆƃroahČŃĜŸn3ĻrcŰŐćnijƕėwĢĂĈacŔmŘėįĉĜş3ƐưŰhěĜnƶİsrĪtĜļċkexƁƅ">Clip Art</option>

  </select><input type="checkbox" checked id="showCursor" onclick="drawCodeArea()">Show Cursor <input type="checkbox" id="drawToCursor" onclick="drawCodeArea()">Draw to Cursor
  <br/>
  <p id="label1">ENTROPX-Code: </p>
  <textarea id="code" cols="50" rows="7" oninput="drawCodeArea()" onkeydown="if (drawToCursor.checked) drawCodeArea()"></textarea>
  <table id="lower">
    <tr>
      <td>q</td>
      <td>p</td>
      <td>i</td>
      <td>x</td>
      <td>l</td>
    </tr>
    <tr>
      <td>b</td>
      <td>e</td>
      <td>s</td>
      <td>t</td>
      <td>d</td>
    </tr>
    <tr>
      <td>u</td>
      <td>m</td>
      <td> </td>
      <td>o</td>
      <td>g</td>
    </tr>
    <tr>
      <td>w</td>
      <td>a</td>
      <td>n</td>
      <td>r</td>
      <td>k</td>
    </tr>
    <tr>
      <td>v</td>
      <td>j</td>
      <td>h</td>
      <td>c</td>
      <td>f</td>
    </tr>
  </table>
  <p id="label2">Compressed ENTROPX-Code: </p>
  <textarea id="lzw" oninput="draw(this.value)" cols="50" rows="3"></textarea>
</div>
<p><strong>Commands a-x and A-X</strong><br/>
  Lowercase letters move the cursor and set a pixel, uppercase letters just move the cursor but don't draw anything. The drawing always starts with an empty 1x1 canvas and the image will recenter and resize itself automatically based on the set pixels.
  <br/>
  <br/><strong>Numbers 0-4096</strong><br/>
  Prepending commands with a number will repeat them that many times - "8o" for example will draw an 8 pixel horizonal line. The maximum limit of 4096 is arbitrary, but put in place to prevent the creation of huge bitmaps in particular when glitching is used.
  <br/>
  <br/><strong>Command '@'</strong><br/>A '@' is the control code to change the current paint color. A '@' can either be followed by a letter or one or more numbers. The letters a-z and A-Z stand for the numbers 0 to 51, so the command '@d' is equivalent to '@3'. The paint color is taken from the currently selected color palette. If the number is bigger than the colors available in the chosen palette the index will wrap around. The default color black is always '@0' or '@a'<br/>
  <br/><strong>Command ':'</strong><br/>A ':' is the control code to change the current color palette. Same like the color chooser a ':' can either be followed by a letter or one or more numbers. The default color palette is ':0' or ':a'  
  <br/><br/><strong>Command '='</strong><br/>
  The '=' is a control code and will use the immediately following numbers or letter to change the cursor to a special brush. So for example the command '=1' will pick brush #1 which is a fat 2x2 pixel - alternative this can be written as '=b'. Note: switching a brush will not also draw it. Furthermore some brushes are drawing around the current pixel, others (like bigger pattern fills) will use the current pixel as the top left corner.<br/>
  <br/><strong>Command '#'</strong><br/>A '#' will draw the brush at the current location. With the default 1 pixel brush this will not make any visible difference, but with the other brushes this allows for some interesting combinations.
  <br/>
  <br/><strong>Commands '+' and '*'</strong><br/>The characters '+' and '*' will apply a bucket fill at the current position using the current color. The '+' will not fill across diagonally connected pixels, whereas the '*' will do that. Since the canvas does not really have a boundary, the bounding box of the currrently set pixels will be the outer limit for the fill.
  <br/>
  <br/><strong>Command '!'</strong><br/>The character '!' has a special meaning: every occurrence of '!' will be replaced with a random letter from the range a-x,A-X, this can be used to introduce random glitches into the pixture. The replacement takes place in the ENTROPX.decode() routine so the resulting image will always be different whenever the string is decoded again.
  <br/>
  <br/><strong>Command '.'</strong><br/>The character '.' will move the cursor back to the starting point.
  <br/>
  <br/><strong>Command ';'</strong><br/>The character '|' will set the current cursor position as the center for the symmetry draw mode, the default center is at 0/0
  <br/>
  <br/><strong>Command '|'</strong><br/>The character '|' followed by a number will set the symmetry mode: 0 = no symmetry, 1 = horizontal, 2 = vertical, 3 = horizonal + vertical. As with the other commands, the numbers can be replaced by letters.
  <br/>
  <br/><strong>Command '()'/'[]'</strong><br/>Anything inside brackets will define a pattern. The pattern will be added to a temporary list of patterns and get an index based on the order of definition. The first pattern will get index 0 or 'a', the second pattern will get index 1 or 'b' and so on. Since you can use patterns inside of patterns it is theoretically possible to create infinite loops which is why there is the MAX_CODE_LENGTH limit that stops the recursion once the resulting code gets too long. Also note that if you are having leading or trailing numbers in a pattern it's a good idea to prepend or append a "_" to avoid merging neighboring numbers that might emerge from consecutive patterns. The difference between the parantheses and the square brackets is that () will not draw the pattern when it is defined whereas [] will also draw it immediately.
  <br/>
  <br/><strong>Command '%'</strong><br/>The '%' followed by a number or letter code will draw a previously defined pattern. If the index is higher than the number of defined patterns it will wrap around. If there are no defined patterns at all the command will be ignored. Since the processing is sequential you cannot use a pattern before it is defined e.g. "(oonnmmss)%0" will work, but "%0(oonnmmss)" will not. Note that inserting a pattern will change the cursor position depending on the relative final cursor position of the stored pattern.
  <br/>
  <br/><strong>Special character '_'</strong><br/>The underscore '_' can be used to separate numbers. For example when first switching a brush and then drawing it repeatedly: '03_4f'. This necessary since '034f' would mean 'use brush 34 and paint it at offset 2/2'. Don't use spaces to separate numbers since those will be cleaned from the code before the rendering.
  <br/>
  <br/><strong>Unknown characters</strong><br/>Unknown letters, spaces or line breaks will be simply ignored and processing skips to the next known command. There are no error messages.
  <br/>
  <br/><strong>Compressed ENTROPX - '$'</strong><br/>ENTROPX has also the option to use LZW compression, the LZW-compressed version starts with a "$" and can be seen in the smaller text edit box. For some unexpected glitch effects you can also directly edit inside the LZW code. Note that compression only works for the entire string, you cannot use compressed strings inside of patterns.<br/>
  <br/><strong>About</strong><br/>ENTROPX is inspired by the concept of Freeman chain codes and PCC line coding as well as L-Systems but is designed to use a charset that is within the printable (and tweetable) code range and offers opportunities for introducing interesting glitches easily. It also adds the option to use colors as well as some other features that are useful for creating pixel art. Another design deliberation was to generate code that can be used to train RNNs or LSTMs for teaching machines how to draw like humans.<br><br>
  When saved to a file, the desired suffix for the ENTROPX format is ".epx" which seems to be already used in the past for some 3d model files but I honestly don't care.
</p>

<div id="brushes">Brushes:</div>
<div id="palettes">Color Palettes:</div>
<footer class="footer">
  <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/80x15.png" /></a> <span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">ENTROPX</span> by <span xmlns:cc="http://creativecommons.org/ns#"
  property="cc:attributionName">Mario Klingemann</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>.
</footer>
              
            
!

CSS

              
                html {
  height: 100%;
  box-sizing: border-box;
}

body {
  position: relative;
  margin: 10px;
  padding-bottom: 6rem;
  min-height: 100%;
  font-family: "Trebuchet MS", Helvetica, sans-serif;
}
h1 {font-weight:normal}
div,p,table {font-size:0.9em;margin:3px; }
p {width:600px;}
td {width:16px;height:16px;text-align:center;}
#code {display:inline-block;}
#lower {color:white;background-color:#000;border:1px solid black;}
#label2 #label1 {display:block;}
footer {font-size:0.8em;margin-top:64px;}

.footer {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 1rem;
  background-color: #efefef;
  text-align: center;
}


textarea { display:inline-block;margin-right:3px; }
canvas {vertical-align:middle;}
#brushes { display:inline-block;height: 30px;font-size:1em;}

              
            
!

JS

              
                /*
ENTROPX by Mario Klingemann v1.0 January 2016
is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License
http://creativecommons.org/licenses/by-nc-sa/4.0/

LZW-encoder/decoder code by Julien Bouquillon
https://gist.github.com/revolunet/843889

Color palettes transcribed from Wikipedia:
https://en.wikipedia.org/wiki/List_of_software_palettes
https://en.wikipedia.org/wiki/List_of_video_game_console_palettes
*/

window["qlib"] = window.qlib || {};

(function() {

  var ENTROPX = function(defaultPixelSize, numberLimit, maxCodeLength) {
    this.init(defaultPixelSize, numberLimit, maxCodeLength)
  }

  var p = ENTROPX.prototype;

  p.init = function(defaultPixelSize, numberLimit, maxCodeLength) {
    if (numberLimit == undefined)
      this.NUMBER_LIMIT = 4096
    else
      this.NUMBER_LIMIT = numberLimit
    if (maxCodeLength == undefined)
      this.MAX_CODE_LENGTH = 200000
    else
      this.MAX_CODE_LENGTH = maxCodeLength
    if (defaultPixelSize == undefined)
      this.DEFAULT_PIXEL_SIZE = 10
    else
      this.DEFAULT_PIXEL_SIZE = defaultPixelSize

    this.code2step = {
      '#': [0, 0],
      'o': [1, 0],
      'r': [1, 1],
      'n': [0, 1],
      'a': [-1, 1],
      'm': [-1, 0],
      'e': [-1, -1],
      's': [0, -1],
      't': [1, -1],
      'g': [2, 0],
      'k': [2, 1],
      'f': [2, 2],
      'c': [1, 2],
      'h': [0, 2],
      'j': [-1, 2],
      'v': [-2, 2],
      'w': [-2, 1],
      'u': [-2, 0],
      'b': [-2, -1],
      'q': [-2, -2],
      'p': [-1, -2],
      'i': [0, -2],
      'x': [1, -2],
      'l': [2, -2],
      'd': [2, -1],
      '=': 'pick brush',
      '@': 'pick color',
      ':': 'pick palette',
      '.': 'origin',
      '_': 'draw',
      '+': 'bucket cross',
      '*': 'bucket star',
      '(': 'start pattern',
      ')': 'end pattern',
      '[': 'start pattern and draw',
      ']': 'end pattern and draw',
      '%': 'insert pattern',
      ';': 'set symm center',
      '|': 'set symm mode'
    }

    this.palettes = [{
        name: 'Default Mono',
        c: [0x000000, 0xffffff]
      }, {
        name: '2 Bit Greyscale',
        c: [0x000000, 0x8b8b8b, 0xcccccc, 0xffffff]
      }, {
        name: '4 Bit Greyscale',
        c: [0x000000, 0x171717, 0x262626, 0x353535, 0x454545, 0x555555, 0x656565, 0x757575, 0x868686, 0x979797, 0xa8a8a8, 0xb9b9b9, 0xcacaca, 0xdbdbdb, 0xededed, 0xffffff]
      }, {
        name: '3 Bit RGB',
        c: [0x000000, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00, 0xffffff]
      }, {
        name: '4 Bit RGBI',
        c: [0x000000, 0x000091, 0x009100, 0x009191, 0x910000, 0x910091, 0x919100, 0xb7b7b7, 0x686868, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00, 0xffffff]
      }, {
        name: 'NES',
        c: [0x000000, 0x101010, 0x7a7a7a, 0xbababa, 0xd6d6d6, 0xfbfbfb, 0xffffff, 0x3200ff, 0x5171fb, 0x7bb6fd, 0xbae0fc, 0x2500bd, 0x4450fb, 0x7882ff, 0xb8b5f9, 0x4625bd, 0x673eff, 0x9274fb, 0xcfb6f9, 0x7d1984, 0xb724ce, 0xdc7afb, 0xe7b7f90, 0x8c2326, 0xbf2f5a, 0xd56298, 0xe1a5bf, 0x8c2905, 0xd24c00, 0xd87e57, 0xe4d0ac, 0x712500, 0xc4650f, 0xe2a33f, 0xf1e0a3, 0x483400, 0x9b7d00, 0xe3ba00, 0xecd971, 0x437500, 0x66b400, 0xc9f700, 0xdef86e, 0x3a6600, 0x5da400, 0x8bd549, 0xcbf6b3, 0x315700, 0x5ea43d, 0x9cf590, 0xccf6d5, 0x284058, 0x4f8486, 0x87e3d5, 0x94f7fb]
      }, {
        name: '3 Level RGB',
        c: [0x000000, 0x000091, 0x0000ff, 0x910000, 0x910091, 0x9100ff, 0xff0000, 0xff0091, 0xff00ff, 0x009100, 0x009191, 0x0091ff, 0x919100, 0x919191, 0x9191ff, 0xff9100, 0xff9191, 0xff91ff, 0x00ff000, 0x00ff91, 0x00ffff, 0x91ff00, 0x91ff91, 0x91ffff, 0xffff00, 0xffff91, 0xffffff]
      }, {
        name: 'C64',
        c: [0x000000, 0xffffff, 0x880000, 0xaaffee, 0xcc44cc, 0x00cc55, 0x0000aa, 0xeeee77, 0xdd8855, 0x664400, 0xff7777, 0x333333, 0x777777, 0xaaff66, 0x0088ff, 0xbbbbbb]
      }, {
        name: 'CGA 0',
        c: [0x000000, 0x00aa00, 0x55ff55, 0x55ff55, 0xff5555, 0xaa5500, 0xffff55]
      }, {
        name: 'CGA 1',
        c: [0x000000, 0xffffff, 0x00aaaa, 0x55ffff, 0xaa00aa, 0xff55ff, 0xaaaaaa]
      }, {
        name: 'CGA 2',
        c: [0x000000, 0xffffff, 0x00aaaa, 0x55ffff, 0xaa0000, 0xff5555, 0xaaaaaa]
      }, {
        name: 'Windows 16',
        c: [0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xC0C0C0, 0x808080, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF]
      }, {
        name: 'Windows 20',
        c: [0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xC0C0C0, 0xC0DCC0, 0xA6CAF0, 0xFFFBF0, 0xA0A0A4, 0x808080, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF]
      }, {
        name: 'Mac 16',
        c: [0x000000, 0x1FB714, 0xFBF305, 0x006412, 0xFF6403, 0x562C05, 0xDD0907, 0x90713A, 0xF20884, 0xC0C0C0, 0x4700A5, 0x808080, 0x0000D3, 0x404040, 0x02ABEA, 0xFFFFFF]
      }, {
        name: 'RISC 16',
        c: [0x000000, 0xFFFFFF, 0xDDDDDD, 0xBBBBBB, 0x999999, 0x777777, 0x555555, 0x333333, 0x004499, 0xEEEE00, 0x00CC00, 0xDD0000, 0xEEEEBB, 0x558800, 0xFFBB00, 0x00BBFF]
      }, {
        name: "ATARI 2600 NTSC",
        c: [0x00000, 0x404040, 0x6c6c6c, 0x909090, 0xb0b0b0, 0xc8c8c8, 0xdcdcdc, 0xececec, 0x444400, 0x646410, 0x848424, 0xa0a034, 0xb8b840, 0xd0d050, 0xe8e85c, 0xfcfc68, 0x702800, 0x844414, 0x985c28, 0xac783c, 0xbc8c4c, 0xcca05c, 0xdcb468, 0xecc878, 0x841800, 0x983418, 0xac5030, 0xc06848, 0xd0805c, 0xe09470, 0xeca880, 0xfcbc94, 0x880000, 0x9c2020, 0xb03c3c, 0xc05858, 0xd07070, 0xe08888, 0xeca0a0, 0xfcb4b4, 0x78005c, 0x8c2074, 0xa03c88, 0xb0589c, 0xc070b0, 0xd084c0, 0xdc9cd0, 0xecb0e0, 0x480078, 0x602090, 0x783ca4, 0x8c58b8, 0xa070cc, 0xb484dc, 0xc49cec, 0xd4b0fc, 0x140084, 0x302098, 0x4c3cac, 0x6858c0, 0x7c70d0, 0x9488e0, 0xa8a0ec, 0xbcb4fc, 0x000088, 0x1c209c, 0x3840b0, 0x505cc0, 0x6874d0, 0x7c8ce0, 0x90a4ec, 0xa4b8fc, 0x00187c, 0x1c3890, 0x3854a8, 0x5070bc, 0x6888cc, 0x7c9cdc, 0x90b4ec, 0xa4c8fc, 0x002c5c, 0x1c4c78, 0x386890, 0x5084ac, 0x689cc0, 0x7cb4d4, 0x90cce8, 0xa4e0fc, 0x003c2c, 0x1c5c48, 0x387c64, 0x509c80, 0x68b494, 0x7cd0ac, 0x90e4c0, 0xa4fcd4, 0x003c00, 0x205c20, 0x407c40, 0x5c9c5c, 0x74b474, 0x8cd08c, 0xa4e4a4, 0xb8fcb8, 0x143800, 0x345c1c, 0x507c38, 0x6c9850, 0x84b468, 0x9ccc7c, 0xb4e490, 0xc8fca4, 0x2c3000, 0x4c501c, 0x687034, 0x848c4c, 0x9ca864, 0xb4c078, 0xccd488, 0xe0ec9c, 0x442800, 0x644818, 0x846830, 0xa08444, 0xb89c58, 0xd0b46c, 0xe8cc7c, 0xfce08c]
      }, {
        name: "ATARI 2600 PAL",
        c: [0x000000, 0x282828, 0x505050, 0x747474, 0x949494, 0xb4b4b4, 0xd0d0d0, 0xececec, 0x805800, 0x947020, 0xa8843c, 0xbc9c58, 0xccac70, 0xdcc084, 0xecd09c, 0xfce0b0, 0x445c00, 0x5c7820, 0x74903c, 0x8cac58, 0xa0c070, 0xb0d484, 0xc4e89c, 0xd4fcb0, 0x703400, 0x885020, 0xa0683c, 0xb48458, 0xc89870, 0xdcac84, 0xecc09c, 0xfcd4b0, 0x006414, 0x208034, 0x3c9850, 0x58b06c, 0x70c484, 0x84d89c, 0x9ce8b4, 0xb0fcc8, 0x700014, 0x882034, 0xa03c50, 0xb4586c, 0xc87084, 0xdc849c, 0xec9cb4, 0xfcb0c8, 0x005c5c, 0x207474, 0x3c8c8c, 0x58a4a4, 0x70b8b8, 0x84c8c8, 0x9cdcdc, 0xb0ecec, 0x70005c, 0x842074, 0x943c88, 0xa8589c, 0xb470b0, 0xc484c0, 0xd09cd0, 0xe0b0e0, 0x003c70, 0x1c5888, 0x3874a0, 0x508cb4, 0x68a4c8, 0x7cb8dc, 0x90ccec, 0xa4e0fc, 0x580070, 0x6c2088, 0x803ca0, 0x9458b4, 0xa470c8, 0xb484dc, 0xc49cec, 0xd4b0fc, 0x002070, 0x1c3c88, 0x3858a0, 0x5074b4, 0x6888c8, 0x7ca0dc, 0x90b4ec, 0xa4c8fc, 0x3c0080, 0x542094, 0x6c3ca8, 0x8058bc, 0x9470cc, 0xa884dc, 0xb89cec, 0xc8b0fc, 0x000088, 0x20209c, 0x3c3cb0, 0x5858c0, 0x7070d0, 0x8484e0, 0x9c9cec, 0xb0b0fc]
      }

    ]

    this.brushes = [
      [
        [0, 0]
      ],
      "onms",
      "#sonnmmss",
      "#sart",
      "#eghu",
      "#poornnammess",
      "ggaurgau",
      "3gauurggauurggauu",
      "4ga3ur3ga3ur3ga3ur3ga3u",
      "3Ooor3ma5o=1n3M#3M#=0knaagtorgees",
      "g7O#a5M#ab4n11o4sw7m=1aGogGoh4u=0ca9O#ePs5m",
      "=1r4grnmUuUuenRgGg=0fkmbumumwo3Xs3o"
    ]

    this.emptyCanvas = {
      width: 1,
      height: 1,
      pixels: [],
      cursor: [0, 0],
      color: 0
    }

    this.drawCommands = 'abcdefghijklmnopqrstuvwxABCDEFGHIJKLMNOPQRSTUVWX'
    this.allowed = this.drawCommands + '_#yzYZ0123456789.!*+()%@:=;|[]'

    for (var i = 0; i < this.brushes.length; i++) {
      if (!Array.isArray(this.brushes[i])) {
        this.brushes[i] = this.decode(this.brushes[i], true)
      }
    }

  }

  p.clean = function(code) {
    var cleancode = ''
    for (var i = 0; i < code.length; i++) {
      if (this.allowed.indexOf(code[i]) > -1)
        cleancode += code[i]
    }
    return cleancode;
  }

  p.decode = function(code, getRawOutput) {
    if (code != undefined && code[0] == "$")
      code = this.lzw_decode(code.substr(1))

    if (code == "" || code == undefined || code == "$") {
      return getRawOutput ? [] : this.emptyCanvas
    }

    var x = 0
    var y = 0
    var minx = 0
    var miny = 0
    var maxx = 0
    var maxy = 0
    var symx = 0
    var symy = 0
    var symMode = 0
    var currentBrush = this.brushes[0]
    var currentPalette = this.palettes[0].c
    var currentColor = currentPalette[0]

    var patterns = []
    var patternBuildingStack = []
    var patternBuildingIndex = -1
    var addPattern = null;

    var pixels = []
    var cleancode = this.clean(code)

    while (cleancode.indexOf("!") > -1)
      cleancode = cleancode.replace("!", this.drawCommands[(Math.random() * 48) | 0])

    code = cleancode
    var p = 0
    while (p < code.length) {

      var mode = 'DRAW'
      if (code[p] == '=') {
        //change brush command
        p++
        if (p == code.length)
          break;
        if (code[p] >= 'a' && code[p] <= 'z') {
          currentBrush = this.brushes[(code.charCodeAt(p) - 'a'.charCodeAt(0)) % this.brushes.length];
          p++
          continue;
        } else if (code[p] >= 'A' && code[p] <= 'Z') {
          currentBrush = this.brushes[(26 + code.charCodeAt(p) - 'A'.charCodeAt(0)) % this.brushes.length];
          p++
          continue;
        }  else if (code[p] >= '0' && code[p] <= '9') {
          mode = 'CHANGE BRUSH'
        }  else if (code[p] == '_') {
          mode = 'CHANGE BRUSH'
          p++
        } else {
          currentBrush = this.brushes[0]
        }
      } else if (code[p] == '@') {
        //change color command
        p++
        if (p == code.length)
          break;

        if (code[p] >= 'a' && code[p] <= 'z') {
          currentColor = currentPalette[(code.charCodeAt(p) - 'a'.charCodeAt(0)) % currentPalette.length];
          p++
          continue;
        } else if (code[p] >= 'A' && code[p] <= 'Z') {
          currentColor = currentPalette[(26 + code.charCodeAt(p) - 'A'.charCodeAt(0)) % currentPalette.length];
          p++
          continue;
        } else if (code[p] == '_') {
          mode = 'CHANGE COLOR'
          p++
        } else if (code[p] >= '0' && code[p] <= '9') {
          mode = 'CHANGE COLOR'
         } else {
          p -= 1;
        }
      } else if (code[p] == ':') {
        //change palette command
        p++
        if (p == code.length)
          break;

        if (code[p] >= 'a' && code[p] <= 'z') {
          currentPalette = this.palettes[(code.charCodeAt(p) - 'a'.charCodeAt(0)) % this.palettes.length].c;
          currentColor = currentPalette[currentColor % currentPalette.length]
          p++
          continue;
        } else if (code[p] >= 'A' && code[p] <= 'Z') {
          currentPalette = this.palettes[(26 + code.charCodeAt(p) - 'A'.charCodeAt(0)) % this.palettes.length].c;
          currentColor = currentPalette[currentColor % currentPalette.length]
          p++
          continue;
        } else if (code[p] == '_') {
          mode = 'CHANGE PALETTE'
          p++
        } else if (code[p] >= '0' && code[p] <= '9') {
          mode = 'CHANGE PALETTE'
        } else {
          p -= 1;
        }
      } else if (code[p] == '%') {
        //insert pattern command
        p++
        if (p == code.length)
          break;

        if (code[p] >= 'a' && code[p] <= 'z') {
          if (patterns.length > 0) {
            var insertPattern = patterns[(code.charCodeAt(p) - 'a'.charCodeAt(0)) % patterns.length];
            console.log("insert",insertPattern)
           console.log("code",code)
            var newcode = code.slice(0, p + 1) + insertPattern + code.slice(p + 1)
            if (newcode.length <= this.MAX_CODE_LENGTH)
              code = newcode
             console.log("code",code)
          }
          p++
          if ( p < code.length) console.log("next is",code[p])
          continue;
        } else if (code[p] >= 'A' && code[p] <= 'Z') {
          if (patterns.length > 0) {
            var insertPattern = patterns[(26 + code.charCodeAt(p) - 'A'.charCodeAt(0)) % patterns.length];
            console.log("insert",insertPattern)
           console.log("code",code)
          
            var newcode = code.slice(0, p + 1) + insertPattern + code.slice(p + 1)
            if (newcode.length <= this.MAX_CODE_LENGTH)
              code = newcode
            console.log("code",code)
          
          }
          p++
          continue;
        } else if (code[p] == '_') {
          mode = 'INSERT PATTERN'
          p++
        } else if (code[p] >= '0' && code[p] <= '9') {
          mode = 'INSERT PATTERN'
        } else {
          p -= 1;
        }
      } else if (code[p] == '|') {
        //set symm mode
        
        p++
        if (p == code.length)
          break;
        if (code[p] >= 'a' && code[p] <= 'z') {
          symMode = (code.charCodeAt(p) - 'a'.charCodeAt(0)) % 4;
          console.log("set symm mode",symMode)
          p++
          continue;
        } else if (code[p] >= 'A' && code[p] <= 'Z') {
          symMode = (26 + code.charCodeAt(p) - 'A'.charCodeAt(0)) % 4;
          console.log("set symm mode",symMode)
          p++
          continue;
        }  else if (code[p] >= '0' && code[p] <= '9') {
          mode = 'SET SYMM MODE'
        }  else if (code[p] == '_') {
          mode = 'SET SYMM MODE'
          p++
        } else {
          symMode = 0
        }
      }

      var n = 0
      var hasNumber = false
      while (p < code.length && code[p] >= '0' && code[p] <= '9') {
        n *= 10
        n += code[p] | 0
        p += 1
        hasNumber = true
      }
      if ( !hasNumber ) n=1
      if (mode == "CHANGE BRUSH") {
        currentBrush = this.brushes[n % this.brushes.length]
        continue;
      } else if (mode == "CHANGE COLOR") {
        currentColor = currentPalette[n % currentPalette.length]
        continue;
      } else if (mode == "CHANGE PALETTE") {
        currentPalette = this.palettes[n % this.palettes.length].c;
        currentColor = currentPalette[currentColor % currentPalette.length]
        continue;
      } else if (mode == "INSERT PATTERN") {
        console.log("INSERT PATTERN")
        if (patterns.length > 0) {
          var insertPattern = patterns[n % patterns.length];
          var newcode = code.slice(0, p) + insertPattern + code.slice(p)
          if (newcode.length <= this.MAX_CODE_LENGTH)
            code = newcode
        }
        continue;
      } else if (mode == "SET SYMM MODE") {
        symMode = n % 4;
        console.log("set symm mode",symMode)
        continue;
      }
      if (n > this.NUMBER_LIMIT)
        n = this.NUMBER_LIMIT
      if (n < 0)
        n = 0
      if (p == code.length)
        break;
      
      step = this.code2step[code[p].toLowerCase()]
      for (var i = 0; i < n; i++) {

        switch (step) {
          case 'origin':
            x = y = 0
            continue;
            break;
          case 'set symm center':
            symx = x;
            symy = y;
            continue;
            break;
          case 'end pattern':
          case 'end pattern and draw':
          case 'pick color':
          case 'pick palette':
          case 'pick brush':
          case 'draw':
          case undefined:
            continue
            break;
          
          case 'insert pattern':
            p++;
            var nn = 0
            while (p < code.length && (code[p] >= '0' && code[p] <= '9' || code[p]=='_')) {
              if ( code[p]!='_')
              {
                nn *= 10
                nn += code[p] | 0
              }
              p += 1
            }
            var insertPattern = patterns[nn % patterns.length];
            while (i < n) {
              var newcode = code.slice(0, p) + insertPattern + code.slice(p)
              if (newcode.length <= this.MAX_CODE_LENGTH)
                code = newcode
                console.log(newcode)
               i++
            }
            p--;
            if ( p < code.length) console.log("insert pattern, next is",code[p])
            continue
            break;

          case 'bucket cross':
            this.bucketFill(pixels, x, y, minx, miny, maxx, maxy, currentColor, currentBrush, false)
            continue
            break;
          case 'bucket star':
            this.bucketFill(pixels, x, y, minx, miny, maxx, maxy, currentColor, currentBrush, true)
            continue
            break;
          case 'start pattern':
          case 'start pattern and draw':
            var pattern = ''
            p++;
            patternBuildingIndex++;
            var append = ''
            while (p < code.length) {
              if (code[p] != '(' && code[p] != ')' && code[p] != '[' && code[p] != ']')
                pattern += '' + code[p]
              else if (code[p] == ')' || code[p] == ']') {
                if (patternBuildingIndex > -1) {
                  patterns.push(pattern)
                  if (code[p] == ']') append += '' + pattern
                  patternBuildingStack[patternBuildingIndex] = pattern
                  patternBuildingIndex--
                }
                if (patternBuildingIndex > -1) {
                  pattern = patternBuildingStack[patternBuildingIndex]
                } else {
                  pattern = ''
                  break;
                }
              } else if (code[p] == '(' || code[p] == '[') {
                if (patternBuildingIndex > -1) {
                  patternBuildingStack[patternBuildingIndex] = pattern
                }
                patternBuildingIndex++;
                pattern = ''
              }
              p++
            }
            if (step== 'start pattern and draw')
            {
                console.log("append",append)
                console.log("code",code)
                
              var newcode = code.slice(0, p+1) + append + code.slice(p+1)
               if (newcode.length <= this.MAX_CODE_LENGTH) code = newcode
                console.log("newcode",newcode)  
                
               }
               
            
            continue;
            break;
          default:
            x += step[0]
            y += step[1]
            break;
        }

        if (code[p] == code[p].toLowerCase()) {
          for (var j = 0; j < currentBrush.length; j++) {
            var px = x + currentBrush[j][0]
            var py = y + currentBrush[j][1]
            pixels.push([px, py, currentColor])
            minx = Math.min(px, minx)
            miny = Math.min(py, miny)
            maxx = Math.max(px, maxx)
            maxy = Math.max(py, maxy)
            if (symMode == 1)
            {
              pixels.push([symx-px, py, currentColor])
              minx = Math.min(symx-px, minx)
              maxx = Math.max(symx-px, maxx)
            }
            if (symMode == 2)
            {
              pixels.push([px, symy-py, currentColor])
              miny = Math.min(symy-py, miny)
              maxy = Math.max(symy-py, maxy)
            }
            if (symMode == 3)
            {
              pixels.push([symx-px, py, currentColor])
              pixels.push([px, symy-py, currentColor])
              pixels.push([symx-px, symy-py, currentColor])
              minx = Math.min(symx-px, minx)
              maxx = Math.max(symx-px, maxx)
              miny = Math.min(symy-py, miny)
              maxy = Math.max(symy-py, maxy)
            }
          }

        }
      }
      p += 1

    }
    if (getRawOutput) {
      return pixels;
    }
    var w = 0
    var h = 0
    for (p in pixels) {
      pixels[p][0] -= minx
      pixels[p][1] -= miny
      w = Math.max(w, pixels[p][0])
      h = Math.max(h, pixels[p][1])
    }
    w += 1
    h += 1
    return {
      width: w,
      height: h,
      pixels: pixels,
      cursor: [x - minx, y - miny],
      color: currentColor
    }

  }

  p.bucketFill = function(pixels, x, y, minx, miny, maxx, maxy, currentColor, currentBrush, fillDiagonally) {
    if (x < minx) minx = x
    if (y < miny) miny = y
    if (x > maxx) maxx = x
    if (y > maxy) maxy = y
    var stackIndex = 0;
    var stack = [
      [x, y]
    ]
    var pIndex = pixels.findIndex(function(e) {
      return e[0] == x && e[1] == y
    })
    if (pIndex > -1)
      var baseColor = pixels[pIndex][2]
    else
      baseColor = -1
    if (baseColor == currentColor) return;

    while (stackIndex < stack.length) {
      var p = stack[stackIndex]
      stackIndex++
      pIndex = pixels.findIndex(function(e) {
        return e[0] == p[0] && e[1] == p[1]
      })
      if (pIndex != -1) {
        pixels[pIndex][2] = currentColor
      } else {
        pixels.push([p[0], p[1], currentColor])
      }
      if (p[0] - 1 >= minx && stack.findIndex(function(e) {
          return e[0] == p[0] - 1 && e[1] == p[1]
        }) == -1) {
        pIndex = pixels.findIndex(function(e) {
          return e[0] == p[0] - 1 && e[1] == p[1]
        })
        if ((pIndex == -1 && baseColor == -1) || (pIndex > -1 && pixels[pIndex][2] == baseColor))
          stack.push([p[0] - 1, p[1]])
      }
      if (p[0] + 1 <= maxx && stack.findIndex(function(e) {
          return e[0] == p[0] + 1 && e[1] == p[1]
        }) == -1) {
        pIndex = pixels.findIndex(function(e) {
          return e[0] == p[0] + 1 && e[1] == p[1]
        })
        if ((pIndex == -1 && baseColor == -1) || (pIndex > -1 && pixels[pIndex][2] == baseColor))
          stack.push([p[0] + 1, p[1]])
      }
      if (p[1] - 1 >= miny && stack.findIndex(function(e) {
          return e[0] == p[0] && e[1] == p[1] - 1
        }) == -1) {
        pIndex = pixels.findIndex(function(e) {
          return e[0] == p[0] && e[1] == p[1] - 1
        })
        if ((pIndex == -1 && baseColor == -1) || (pIndex > -1 && pixels[pIndex][2] == baseColor))
          stack.push([p[0], p[1] - 1])
      }
      if (p[1] + 1 <= maxy && stack.findIndex(function(e) {
          return e[0] == p[0] && e[1] == p[1] + 1
        }) == -1) {
        pIndex = pixels.findIndex(function(e) {
          return e[0] == p[0] && e[1] == p[1] + 1
        })
        if ((pIndex == -1 && baseColor == -1) || (pIndex > -1 && pixels[pIndex][2] == baseColor))
          stack.push([p[0], p[1] + 1])
      }
      if (fillDiagonally) {
        if (p[0] - 1 >= minx && p[1] - 1 >= miny && stack.findIndex(function(e) {
            return e[0] == p[0] - 1 && e[1] == p[1] - 1
          }) == -1) {
          pIndex = pixels.findIndex(function(e) {
            return e[0] == p[0] - 1 && e[1] == p[1] - 1
          })
          if ((pIndex == -1 && baseColor == -1) || (pIndex > -1 && pixels[pIndex][2] == baseColor))
            stack.push([p[0] - 1, p[1] - 1])
        }
        if (p[0] - 1 >= minx && p[1] + 1 <= maxy && stack.findIndex(function(e) {
            return e[0] == p[0] - 1 && e[1] == p[1] + 1
          }) == -1) {
          pIndex = pixels.findIndex(function(e) {
            return e[0] == p[0] - 1 && e[1] == p[1] + 1
          })
          if ((pIndex == -1 && baseColor == -1) || (pIndex > -1 && pixels[pIndex][2] == baseColor))
            stack.push([p[0] - 1, p[1] + 1])
        }
        if (p[0] + 1 <= maxx && p[1] + 1 <= maxy && stack.findIndex(function(e) {
            return e[0] == p[0] + 1 && e[1] == p[1] + 1
          }) == -1) {
          pIndex = pixels.findIndex(function(e) {
            return e[0] == p[0] + 1 && e[1] == p[1] + 1
          })
          if ((pIndex == -1 && baseColor == -1) || (pIndex > -1 && pixels[pIndex][2] == baseColor))
            stack.push([p[0] + 1, p[1] + 1])
        }
        if (p[0] + 1 <= maxx && p[1] - 1 >= miny && stack.findIndex(function(e) {
            return e[0] == p[0] + 1 && e[1] == p[1] - 1
          }) == -1) {
          pIndex = pixels.findIndex(function(e) {
            return e[0] == p[0] + 1 && e[1] == p[1] - 1
          })
          if ((pIndex == -1 && baseColor == -1) || (pIndex > -1 && pixels[pIndex][2] == baseColor))
            stack.push([p[0] + 1, p[1] - 1])
        }
      }
    }
  }

  p.renderToCanvas = function(epxData, scale, canvas, autoScale, padding, renderCursor) {
    if (scale == undefined) scale = this.DEFAULT_PIXEL_SIZE
    if (padding == undefined) padding = 0
    var padx = 0
    var pady = 0

    if (canvas == undefined) {
      var canvas = document.createElement("canvas")
      canvas.width = Math.min(this.NUMBER_LIMIT, scale * epxData.width + padding * 2)
      canvas.height = Math.min(this.NUMBER_LIMIT, scale * epxData.height + padding * 2)

    } else {
      if (autoScale) {
        scale = Math.max(1, Math.min((canvas.width - 2 * padding) / epxData.width, (canvas.height - 2 * padding) / epxData.height)) | 0
      }
      var padx = (padding + 0.5 * (canvas.width - scale * epxData.width)) | 0
      var pady = (padding + 0.5 * (canvas.height - scale * epxData.height)) | 0

    }
    var ctx = canvas.getContext("2d");
    ctx.fillStyle = '#fff';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = '#000';
    var lastColor = 0
    var p = epxData.pixels
    for (var i in p) {
      if (lastColor != p[i][2]) {
        lastColor = p[i][2]
        ctx.fillStyle = '#' + ("000000" + (lastColor & 0xffffff).toString(16)).slice(-6);
      }
      ctx.fillRect(padx + p[i][0] * scale, pady + p[i][1] * scale, scale, scale);
    }
    if (renderCursor) {
      ctx.strokeStyle = '#000';
      ctx.strokeRect(padx + epxData.cursor[0] * scale - 1.5, pady + epxData.cursor[1] * scale - 1.5, scale + 3, scale + 3)

      ctx.strokeStyle = '#' + ("000000" + (epxData.color & 0xffffff).toString(16)).slice(-6);
      ctx.strokeRect(padx + epxData.cursor[0] * scale, pady + epxData.cursor[1] * scale, scale, scale)
      ctx.strokeStyle = '#ffffff';
      ctx.strokeRect(padx + epxData.cursor[0] * scale + 1.5, pady + epxData.cursor[1] * scale + 1.5, scale - 3, scale - 3)
    }
    return canvas
  }

  p.decodeAndRender = function(code, scale, canvas, autoScale, padding, renderCursor) {
    return this.renderToCanvas(this.decode(code), scale, canvas, autoScale, padding, renderCursor)
  }

  p.lzw_encode = function(s) {
    if (s == '' || s == undefined) return ''
    var dict = {};
    var data = (s + "").split("");
    var out = [];
    var currChar;
    var phrase = data[0];
    var code = 256;
    for (var i = 1; i < data.length; i++) {
      currChar = data[i];
      if (dict[phrase + currChar] != null) {
        phrase += currChar;
      } else {
        out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));
        dict[phrase + currChar] = code;
        code++;
        phrase = currChar;
      }
    }
    out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));
    for (var i = 0; i < out.length; i++) {
      out[i] = String.fromCharCode(out[i]);
    }
    return out.join("");
  }

  p.lzw_decode = function(s) {
    var dict = {};
    var data = (s + "").split("");
    var currChar = data[0];
    var oldPhrase = currChar;
    var out = [currChar];
    var code = 256;
    var phrase;
    for (var i = 1; i < data.length; i++) {
      var currCode = data[i].charCodeAt(0);
      if (currCode < 256) {
        phrase = data[i];
      } else {
        phrase = dict[currCode] ? dict[currCode] : (oldPhrase + currChar);
      }
      out.push(phrase);
      currChar = phrase.charAt(0);
      dict[code] = oldPhrase + currChar;
      code++;
      oldPhrase = phrase;
    }
    return out.join("");
  }

  qlib["ENTROPX"] = ENTROPX;

}());

//-----------------------------------------------------------------------------------------------
function drawCodeArea()
{
  if (drawToCursor.checked)
    draw(code.value.substring(0,code.selectionStart))
  else
    draw(code.value)
}

function draw(text) {
  
  
  renderer.decodeAndRender(text, undefined, output, true, 5, showCursor.checked)
  if (!drawToCursor.checked)
  {
  if (text[0] == "$") {
    lzw_output.value = text
    code.value = renderer.lzw_decode(text.substr(1))
  } else {
    lzw_output.value = "$" + renderer.lzw_encode(text)
    code.value = text
  }
  }
  label2.innerHTML = "Compressed ENTROPX-Code - " + lzw_output.value.length + " chars:"
  label1.innerHTML = "ENTROPX-Code - " + code.value.length + " chars:"
}

var renderer = new qlib.ENTROPX()
var output = document.getElementById("output")
var lzw_output = document.getElementById("lzw")
var code = document.getElementById("code")
var label1 = document.getElementById("label1")
var label2 = document.getElementById("label2")
var brushes = document.getElementById("brushes")
var palettes = document.getElementById("palettes")

var holder = document.getElementById("holder")
var presets = document.getElementById("presets");
var showCursor = document.getElementById("showCursor");
var drawToCursor = document.getElementById("drawToCursor");

var brushesPerRow = 8
var table = document.createElement('table');
for (var i = 0; i < renderer.brushes.length; i++) {
  if (i % brushesPerRow == 0) {
    var row1 = table.insertRow();
    var row2 = table.insertRow();
  }
  var cell1 = row1.insertCell()
  var cell2 = row2.insertCell()
  cell1.style.width = "50px"
  cell2.style.width = "50px"

  var p = document.createTextNode("=" + i + ":");
  cell2.appendChild(p)
  cell1.appendChild(renderer.decodeAndRender("=" + i + "#", 5, null, false, 3))
}
brushes.appendChild(table)
brushes.style.height = "200px"

var table = document.createElement('table');
table.style.width = "600px"
for (var i = 0; i < renderer.palettes.length; i++) {
  var row = table.insertRow();
  var cell = row.insertCell()
    //cell.style.width = "100px"
  cell.style.textAlign = "left"

  var p = document.createTextNode(":" + i + " (" + renderer.palettes[i].name + "): ");
  cell.appendChild(p)
  cell.style.verticalAlign = "top"
  var cell2 = row.insertCell()
    //cell2.style.width = "300px"

  cell2.style.textAlign = "left"
  var swatchesPerRow = 16

  var pstring = ":" + i + "_M"
  for (var j = 0; j < renderer.palettes[i].c.length; j++) {
    if (j % swatchesPerRow == 0 && j > 0) {
      var y = (j / swatchesPerRow) | 0;
      var v = "N"
      if (y == 2)
        v += "N"
      else if (y > 2)
        v = y + v
      pstring += "." + v + "M"
    }

    pstring += "@" + j + "o"

  }
  var cnv = renderer.decodeAndRender(pstring, 16, null, false, 0, false)
  cnv.style.border = "1px solid #000";
  cnv.style.padding = "0"
    //cnv.style.margin = "0"
  cell2.appendChild(cnv)
}
palettes.appendChild(table)

presets.selectedIndex = 0
presets.onchange()

/*
Yes, you read correctly, this code is currently licenced under creative commons and not some kind of "do whatever you like" license. This means that if you reuse this code or language soemwhere else or make a derivate I want to see something like "using ENTROPX by Mario Klingemann" visible on that page - and no, not just somewhere hidden in the source code. Yes, I am that vain. And just so we understand each other: no commercial use wihout my written permisson.
*/
              
            
!
999px

Console