css Audio - Active file-generic CSS - Active Generic - Active HTML - Active JS - Active SVG - Active Text - Active file-generic Video - Active header Love html icon-new-collection icon-person icon-team numbered-list123 pop-out spinner split-screen star tv

Pen Settings

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

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

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

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

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.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              
body { margin: 0; overflow: hidden; background: #111; color: #ccc; }
#canvas {
  position: fixed;
  top:50%;
  bottom: 0;
  left: 50%;
  right: 0;

  width: 200vmin;
  height: 100vmin;

  transform:translate(-50%, -25%);

  image-rendering: optimizeSpeed;
  image-rendering: -moz-crisp-edges;
  image-rendering: -webkit-optimize-contrast;
  image-rendering: -o-crisp-edges;
  image-rendering: pixelated;
  -ms-interpolation-mode: nearest-neighbor;

  cursor: none;

  margin: auto;


}

//ctx.canvas.style = 'position:fixed; top: 50%; left:50%;width:!00vmin;height:100vmin;transform:translate(-50%, -50%)';

            
          
!
            
              var GAME = (function(){
 var states = {};

assets = {
  // font:{
  //      string: "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_!@.'\"?/<()",
  //
  //   //    bin: '~cQz??I#_CH?|Ba<?FcdO~Rz90B?uFs,q:bwQKH2?h}/A`}yHc.FT$1VW"Q'+
  //   //  'F!D>#g"xtR|!?Ayx=_"t.Eg"x.:bpH!xo-j}/TC mQ@" BB',
  //   //  bin: [
  //   //    ["111111111011111111001111111111111111000111111111111000110000100011000101110111100111011110011111111110001100011000110001100011111100100011101111010010111110111111111011100111001110000000010011111010100000000110110110111000001000100001001000"],
  //   //    ["100011000110000100101000010000100001000100100000101001010000110111100110001100011000110001100000010010001100011000101010010100001001100100010000110010100001000000001100011000110001000000010010001111110000000010010011000100010001000010000100"],
  //   //    ["111111111010000100011110011100101111111100100000101110010000101011010110001100101000111110011100010010001100011010100100001000010000100000100011011111111101111000010011100111110001000000010010111010100000000100100100011000100010000010000100"],
  //   //    ["100011000110000100011000010000100011000100100100101001010000100011001110001111001001010001000010010010001010101010101010001000100000100001000000100010000011000100100100010000110001000000000010101111110000000000000000000001000001000010000100"],
  //   //    ["100011111001111111101111110000111111000111111111101000111111100011000101110100000111110001111100010001110001000111010001001001111101110111111111000010111100111000100011100111001110111110010010111010100010000000000000010010000000100001001000"]
  //   //     ],
  //       // bigString: "11111100011111110001100011111010001111101000111110111111000010000100000111111100100101000110001111101111110000111001000011111111111000"+
  //       // "0111001000010000111111000010111100011111110001100011111110001100011111100100001000010011111111110001000010100101111010001100101110010010100011000"+
  //       // "0100001000010000111111000111011101011000110001100011100110101100111000101110100011000110001011101111010001100101110010000011101000110001100100111"+
  //       // "1111101000111110100011000101111100000111000001111101111100100001000010000100100011000110001100010111010001100011000101010001001000110001101011010"+
  //       // "1011101000101010001000101010001100010101000100001000010011111000100010001000111110010001100001000010001110011101000100010001001111111110000010011"+
  //       // "0000011111010010100101111100010000101111110000111100000111110011111000011110100010111011111000010001000100001000111010001011101000101110011101000"+
  //       // "1011110000101110011101000110001100010111000000000000000000000111110010000100001000000000100111111000110111101011011101010111110101011111010100000"+
  //       // "000000000000000000100001100001000100000000000011011010011001000000000000111010001001100000000100000010001000100010001000000010001000100000100000100001000100001000010000010"
  //
  // },
  laser: {
    "osc1_oct": 7,
    "osc1_det": 0,
    "osc1_detune": 0,
    "osc1_xenv": 1,
    "osc1_vol": 255,
    "osc1_waveform": 3,
    "osc2_oct": 8,
    "osc2_det": 0,
    "osc2_detune": 0,
    "osc2_xenv": 1,
    "osc2_vol": 255,
    "osc2_waveform": 0,
    "noise_fader": 61,
    "env_attack": 22,
    "env_sustain": 22,
    "env_release": 21759,
    "env_master": 255,
    "fx_filter": 3,
    "fx_freq": 4067,
    "fx_resonance": 176,
    "fx_delay_time": 4,
    "fx_delay_amt": 12,
    "fx_pan_freq": 2,
    "fx_pan_amt": 84,
    "lfo_osc1_freq": 0,
    "lfo_fx_freq": 1,
    "lfo_freq": 3,
    "lfo_amt": 96,
    "lfo_waveform": 0
  },
};

//--this gets wrapped in a closure, so no namespace object, compresses better.
const WIDTH =     512;
const HEIGHT =    256;
var
C =               document.getElementById('canvas');
ctx =             C.getContext('2d'),



renderTarget =    0x00000,
renderSource =    0x20000,

//Richard Fhager's DB32 Palette http://http://pixeljoint.com/forum/forum_posts.asp?TID=16247
//ofcourse you can change this to whatever you like, up to 256 colors.
//one GOTCHA: colors are stored 0xAABBGGRR, so you'll have to flop the values from your typical hex colors.

colors =          [0xff000000, 0xff342022, 0xff3c2845, 0xff313966, 0xff3b568f, 0xff2671df, 0xff66a0d9, 0xff9ac3ee, 0xff36f2fb,
                   0xff50e599, 0xff30be6a, 0xff6e9437, 0xff2f694b, 0xff244b52, 0xff393c32, 0xff743f3f, 0xff826030, 0xffe16e5b,
                   0xffff9b63, 0xffe4cd5f, 0xfffcdbcb, 0xffffffff, 0xffb7ad9b, 0xff877e84, 0xff6a6a69, 0xff525659, 0xff8a4276,
                   0xff3232ac, 0xff6357d9, 0xffba7bd7, 0xff4a978f, 0xff306f8a],

//default palette index
palDefault =      [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],

//active palette index. maps to indices in colors[]. can alter this whenever for palette effects.
pal =             [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31];

ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;

C.width = WIDTH;
C.height = HEIGHT;
var imageData =       ctx.getImageData(0, 0, 512, 256),
buf =             new ArrayBuffer(imageData.data.length),
buf8 =            new Uint8Array(buf),
data =            new Uint32Array(buf),
ram =             new Uint8ClampedArray(0x100000);

//--------------graphics functions----------------
      function clear(color){
        ram.fill(color, renderTarget, renderTarget + 0x20000);
      }

      function pset(x, y, color) { //from colors array, 0-31
        x = x|0; y = y|0; color = color|0;

        if (x > -1 && x < WIDTH && y > -1 && y < HEIGHT) {
          ram[renderTarget + (y * WIDTH + x)] = color;
        }
      }

      function line(x1, y1, x2, y2, color) {

        x1 = x1|0;
        x2 = x2|0;
        y1 = y1|0;
        y2 = y2|0;

        var dy = (y2 - y1);
        var dx = (x2 - x1);
        var stepx, stepy;

        if (dy < 0) {
          dy = -dy;
          stepy = -1;
        } else {
          stepy = 1;
        }
        if (dx < 0) {
          dx = -dx;
          stepx = -1;
        } else {
          stepx = 1;
        }
        dy <<= 1;        // dy is now 2*dy
        dx <<= 1;        // dx is now 2*dx

        pset(x1, y1, color);
        if (dx > dy) {
          var fraction = dy - (dx >> 1);  // same as 2*dy - dx
          while (x1 != x2) {
            if (fraction >= 0) {
              y1 += stepy;
              fraction -= dx;          // same as fraction -= 2*dx
            }
            x1 += stepx;
            fraction += dy;              // same as fraction -= 2*dy
            pset(x1, y1, color);
          }
          ;
        } else {
          fraction = dx - (dy >> 1);
          while (y1 != y2) {
            if (fraction >= 0) {
              x1 += stepx;
              fraction -= dy;
            }
            y1 += stepy;
            fraction += dx;
            pset(x1, y1, color);
          }
        }

      }

      function circle(xm, ym, r, color) {
        var x = -r, y = 0, err = 2 - 2 * r;
        /* II. Quadrant */
        do {
          pset(xm - x, ym + y, color);
          /*   I. Quadrant */
          pset(xm - y, ym - x, color);
          /*  II. Quadrant */
          pset(xm + x, ym - y, color);
          /* III. Quadrant */
          pset(xm + y, ym + x, color);
          /*  IV. Quadrant */
          r = err;
          if (r <= y) err += ++y * 2 + 1;
          /* e_xy+e_y < 0 */
          if (r > x || err > y) err += ++x * 2 + 1;
          /* e_xy+e_x > 0 or no 2nd y-step */

        } while (x < 0);
      }

      function fillCircle(xm, ym, r, color) {
        if(r < 0) return;
        xm = xm|0; ym = ym|0, r = r|0; color = color|0;
        var x = -r, y = 0, err = 2 - 2 * r;
        /* II. Quadrant */
        do {
          line(xm-x, ym-y, xm+x, ym-y, color);
          line(xm-x, ym+y, xm+x, ym+y, color);
          r = err;
          if (r <= y) err += ++y * 2 + 1;
          if (r > x || err > y) err += ++x * 2 + 1;
        } while (x < 0);
      }

      function rect(x, y, w, h, color) {
        x1 = x|0;
        y1 = y|0;
        x2 = (x+w)|0;
        y2 = (y+h)|0;


        line(x1,y1, x2, y1, color);
        line(x2, y1, x2, y2, color);
        line(x1, y2, x2, y2, color);
        line(x1, y1, x1, y2, color);
      }

      function fr(x, y, w, h, color) {  //draw a filled rectangle

        x1 = x|0;
        y1 = y|0;
        x2 = (x+w)|0;
        y2 = (y+h)|0;

        var i = Math.abs(y2 - y1);
        line(x1, y1, x2, y1, color);

        if(i > 0){
          while (--i) {
            line(x1, y1+i, x2, y1+i, color);
          }
        }

        line(x1,y2, x2, y2, color);
      }

      function triangle(x1, y1, x2, y2, x3, y3, color) {
        line(x1,y1, x2,y2, color);
        line(x2,y2, x3,y3, color);
        line(x3,y3, x1,y1, color);
      }

      function fillTriangle( x1, y1, x2, y2, x3, y3, color ) {

        var canvasWidth = 256;
        // http://devmaster.net/forums/topic/1145-advanced-rasterization/
        // 28.4 fixed-point coordinates
        var x1 = Math.round( 16 * x1 );
        var x2 = Math.round( 16 * x2 );
        var x3 = Math.round( 16 * x3 );
        var y1 = Math.round( 16 * y1 );
        var y2 = Math.round( 16 * y2 );
        var y3 = Math.round( 16 * y3 );
        // Deltas
        var dx12 = x1 - x2, dy12 = y2 - y1;
        var dx23 = x2 - x3, dy23 = y3 - y2;
        var dx31 = x3 - x1, dy31 = y1 - y3;
        // Bounding rectangle
        var minx = Math.max( ( Math.min( x1, x2, x3 ) + 0xf ) >> 4, 0 );
        var maxx = Math.min( ( Math.max( x1, x2, x3 ) + 0xf ) >> 4, 256 );
        var miny = Math.max( ( Math.min( y1, y2, y3 ) + 0xf ) >> 4, 0 );
        var maxy = Math.min( ( Math.max( y1, y2, y3 ) + 0xf ) >> 4, 256 );
        // Block size, standard 8x8 (must be power of two)
        var q = 8;
        // Start in corner of 8x8 block
        minx &= ~(q - 1);
        miny &= ~(q - 1);
        // Constant part of half-edge functions
        var c1 = -dy12 * x1 - dx12 * y1;
        var c2 = -dy23 * x2 - dx23 * y2;
        var c3 = -dy31 * x3 - dx31 * y3;
        // Correct for fill convention
        if ( dy12 > 0 || ( dy12 == 0 && dx12 > 0 ) ) c1 ++;
        if ( dy23 > 0 || ( dy23 == 0 && dx23 > 0 ) ) c2 ++;
        if ( dy31 > 0 || ( dy31 == 0 && dx31 > 0 ) ) c3 ++;
        // Note this doesn't kill subpixel precision, but only because we test for >=0 (not >0).
        // It's a bit subtle. :)
        c1 = (c1 - 1) >> 4;
        c2 = (c2 - 1) >> 4;
        c3 = (c3 - 1) >> 4;
        // Set up min/max corners
        var qm1 = q - 1; // for convenience
        var nmin1 = 0, nmax1 = 0;
        var nmin2 = 0, nmax2 = 0;
        var nmin3 = 0, nmax3 = 0;
        if (dx12 >= 0) nmax1 -= qm1*dx12; else nmin1 -= qm1*dx12;
        if (dy12 >= 0) nmax1 -= qm1*dy12; else nmin1 -= qm1*dy12;
        if (dx23 >= 0) nmax2 -= qm1*dx23; else nmin2 -= qm1*dx23;
        if (dy23 >= 0) nmax2 -= qm1*dy23; else nmin2 -= qm1*dy23;
        if (dx31 >= 0) nmax3 -= qm1*dx31; else nmin3 -= qm1*dx31;
        if (dy31 >= 0) nmax3 -= qm1*dy31; else nmin3 -= qm1*dy31;
        // Loop through blocks
        var linestep = (canvasWidth-q);
        for ( var y0 = miny; y0 < maxy; y0 += q ) {
          for ( var x0 = minx; x0 < maxx; x0 += q ) {
            // Edge functions at top-left corner
            var cy1 = c1 + dx12 * y0 + dy12 * x0;
            var cy2 = c2 + dx23 * y0 + dy23 * x0;
            var cy3 = c3 + dx31 * y0 + dy31 * x0;
            // Skip block when at least one edge completely out
            if (cy1 < nmax1 || cy2 < nmax2 || cy3 < nmax3) continue;
            // Offset at top-left corner
            var offset = (x0 + y0 * canvasWidth);
            // Accept whole block when fully covered
            if (cy1 >= nmin1 && cy2 >= nmin2 && cy3 >= nmin3) {
              for ( var iy = 0; iy < q; iy ++ ) {
                for ( var ix = 0; ix < q; ix ++, offset ++ ) {
                  ram[renderTarget + offset] = color;
                }
                offset += linestep;
              }
            } else { // Partially covered block
              for ( var iy = 0; iy < q; iy ++ ) {
                var cx1 = cy1;
                var cx2 = cy2;
                var cx3 = cy3;
                for ( var ix = 0; ix < q; ix ++ ) {
                  if ( (cx1 | cx2 | cx3) >= 0 ) {
                    ram[renderTarget + offset] = color;
                  }
                  cx1 += dy12;
                  cx2 += dy23;
                  cx3 += dy31;
                  offset ++;
                }
                cy1 += dx12;
                cy2 += dx23;
                cy3 += dx31;
                offset += linestep;
              }
            }
          }
        }
      }

      function spr(sx = 0, sy = 0, sw = 16, sh = 16, x=0, y=0, flipx = false, flipy = false){


        for(var i = 0; i < sh; i++){

          for(var j = 0; j < sw; j++){

            if(y+i < HEIGHT && x+j < WIDTH && y+i > -1 && x+j > -1){
              if(flipx & flipy){

                if(ram[(renderSource + ( ( sy + (sh-i) )*WIDTH+sx+(sw-j)))] > 0) {

                  ram[ (renderTarget + ((y+i)*WIDTH+x+j)) ] = pal[ ram[(renderSource + ((sy+(sh-i))*WIDTH+sx+(sw-j)))] ];

                }

              }
              else if(flipy && !flipx){

                if(ram[(renderSource + ( ( sy + (sh-i) )*WIDTH+sx+j))] > 0) {

                  ram[ (renderTarget + ((y+i)*WIDTH+x+j)) ] = ram[(renderSource + ((sy+(sh-i))*WIDTH+sx+j))];

                }

              }
              else if(flipx && !flipy){

                if(ram[(renderSource + ((sy+i)*WIDTH+sx+(sw-j)))] > 0) {

                  ram[ (renderTarget + ((y+i)*WIDTH+x+j)) ] = ram[(renderSource + ((sy+i)*WIDTH+sx+(sw-j)))];

                }

              }
              else if(!flipx && !flipy){

                if(ram[(renderSource + ((sy+i)*WIDTH+sx+j))] > 0) {

                  ram[ (renderTarget + ((y+i)*WIDTH+x+j)) ] = pal[ ram[(renderSource + ((sy+i)*WIDTH+sx+j))] ];

                }

              }
            }
          }
        }
      }

      function sspr(sx = 0, sy = 0, sw = 16, sh = 16, x=0, y=0, dw=16, dh=16, flipx = false, flipy = false){

        var xratio = sw / dw;
        var yratio = sh / dh;

        for(var i = 0; i < dh; i++){
          for(var j = 0; j < dw; j++){

            px = (j*xratio)|0;
            py = (i*yratio)|0;

            if(y+i < HEIGHT && x+j < WIDTH && y+i > -1 && x+j > -1) {
              if (ram[(renderSource + ((sy + py) * WIDTH + sx + px))] > 0) {
                ram[(renderTarget + ((y + i) * WIDTH + x + j))] = ram[(renderSource + ((sy + py) * WIDTH + sx + px))]
              }
            }

          }
        }


      }

      function rspr( sx, sy, sw, sh, destCenterX, destCenterY, scale, angle ){

        angle = angle * 0.0174533 //convert to radians in place
        var sourceCenterX = sx + sw / 2;
        var sourceCenterY = sy + sh / 2;

       var destWidth = sw * scale;
        var destHeight = sh * scale;

       var halfWidth = (destWidth / 2 * 1.41421356237)|0 + 5;  //area will always be square, hypotenuse trick
        var halfHeight = (destHeight / 2 * 1.41421356237)|0 + 5;

       var startX = -halfWidth;
        var endX = halfWidth;

       var startY = -halfHeight;
        var endY = halfHeight;

       var scaleFactor = 1.0 / scale;

       var cos = Math.cos(-angle) * scaleFactor;
       var sin = Math.sin(-angle) * scaleFactor;

       for(let y = startY; y < endY; y++){
          for(let x = startX; x < endX; x++){

           let u = sourceCenterX + Math.round(cos * x + sin * y);
            let v = sourceCenterY + Math.round(-sin * x  + cos * y);

           let drawX = (x + destCenterX)|0;
            let drawY = (y + destCenterY)|0;

           if(u >= 0 && v >= 0 && u < sw && v < sh){
              if( ram[ (renderSource + (v * WIDTH + u)) ] > 0) {
                ram[(renderTarget + (drawY * WIDTH + drawX)) ] = ram[(renderSource + ( v * WIDTH + u )) ]
              }
            }

         } //end x loop

       } //end outer y loop
      }

      function checker(w, h, nRow, nCol, color) {
        //var w = 256;
        //var h = 256;
        var x = 0;
        var y = 0;

        nRow = nRow || 8;    // default number of rows
        nCol = nCol || 8;    // default number of columns

        w /= nCol;            // width of a block
        h /= nRow;            // height of a block

        for (var i = 0; i < nRow; ++i) {
          for (var j = 0, col = nCol / 2; j < col; ++j) {
            x = 2 * j * w + (i % 2 ? 0 : w);
            y = i * h;
            fr(x, y, w-1, h-1, color);
          }
        }
      }

    // util: {
    //
    //   toPolarScreen(p){
    //     let degrees = (360/256) * p.x * 0.0174533;
    //     let radius = p.y / 2;
    //     return util.polarToPoint(degrees, radius);
    //   },
    //
    //   norm(value, min, max){
    //     return (value - min) / (max - min);
    //   },
    //
    //   dist(x0, y0, x1, y1) {
    //     if(arguments.length === 2) {
    //       return this.dist(x0.x, x0.y, y0.x, y0.y);
    //     }
    //     var dx = x1 - x0,
    //     dy = y1 - y0;
    //     return Math.sqrt(dx * dx + dy * dy);
    //   },
    //
    //
    //   polarToPoint(angle, radius) {
    //     return {
    //       x: Math.cos(angle) * radius,
    //       y: Math.sin(angle) * radius
    //     };
    //   },
    //
    //   pointToPolar(p) {
    //     return {
    //       angle: Math.atan2(p.y, p.x),
    //       radius: this.magnitude(p)
    //     };
    //   },
    //
    //   magnitude(p) {
    //     return this.dist(0, 0, p.x, p.y);
    //   },
    //
    //   scale(p) {
    //
    //   }
    //
    //
    // },

    function render() {

      var i = 0x20000;  // display is first 0x20000 bytes of ram

      while (i--) {
        /*
        data is 32bit view of final screen buffer
        for each pixel on screen, we look up it's color and assign it
        */
        data[i] = colors[pal[ram[i]]];

      }

      imageData.data.set(buf8);

      ctx.putImageData(imageData, 0, 0);

    }

    playSound = function(buffer, playbackRate, pan, loop) {

      var source = audioCtx.createBufferSource();
      var gainNode = audioCtx.createGain();
      var panNode = audioCtx.createStereoPanner();

      source.buffer = buffer;
      source.connect(panNode);
      panNode.connect(gainNode);
      gainNode.connect(audioCtx.destination);

      //gainNode.connect(audioCtx.destination);
      source.playbackRate.value = playbackRate;
      source.loop = loop;
      gainNode.gain.value = 1;
      panNode.pan.value = pan;
      source.start();
      return {volume: gainNode, sound: source};
    }


init = function(){

  last = 0;
  dt = 0;
  now = 0;
  t = 0;
  moveX = 0;
  speedFactor = .6;
  songTrigger = false;
  state = 'menu';
  demostate = 0;
  audioCtx = new AudioContext;
  fontString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_!@.'\"?/<()";

  fontBitmap = "11111100011111110001100011111010001111101000111110111111000010000100000111111100100101000110001111101111110000111001000011111111111000"+
  "0111001000010000111111000010111100011111110001100011111110001100011111100100001000010011111111110001000010100101111010001100101110010010100011000"+
  "0100001000010000111111000111011101011000110001100011100110101100111000101110100011000110001011101111010001100101110010000011101000110001100100111"+
  "1111101000111110100011000101111100000111000001111101111100100001000010000100100011000110001100010111010001100011000101010001001000110001101011010"+
  "1011101000101010001000101010001100010101000100001000010011111000100010001000111110010001100001000010001110011101000100010001001111111110000010011"+
  "0000011111010010100101111100010000101111110000111100000111110011111000011110100010111011111000010001000100001000111010001011101000101110011101000"+
  "1011110000101110011101000110001100010111000000000000000000000111110010000100001000000000100111111000110111101011011101010111110101011111010100000"+
  "000000000000000000100001100001000100000000000011011010011001000000000000111010001001100000000100000010001000100010001000000010001000100000100000100001000100001000010000010"


//   asciiBinary = '~cQz??I#_CH?|Ba<?FcdO~Rz90B?uFs,q:bwQKH2?h}/A`}yHc.FT$1VW"Q'+
// 'F!D>#g"xtR|!?Ayx=_"t.Eg"x.:bpH!xo-j}/TC mQ@" BB';

  //console.log( getCharacter('A') );

  //console.log( assets.font.string.length );
  //console.log( flattenArray() );

  //console.log( binToAscii(assets.font.bigString) );

  //console.log( asciiToBin('~cQz??I#_CH?|Ba<?FcdO~Rz90B?uFs,q:bwQKH2?h}/A`}yHc.FT$1VW"Q'+ 'F!D>#g"xtR|!?Ayx=_"t.Eg"x.:bpH!xo-j}/TC mQ@" BB'));

  //assets.font.bigString = asciiToBin(assets.font.bin);

  //fontBitmap = (function(asciiBinary,b,i){b="";for(i in asciiBinary)b+=(1e7+s.charCodeAt(i).toString(2)).slice(-7);return b})();


  bulletPool = new Pool(100, Particle);

  sounds = {};

  stats = new Stats();
  document.body.appendChild( stats.dom );

  bulletPool.init();

  player.init();

  soundInit();

  eventInit();

  //init vid capture
  //capturer = new CCapture( {format: 'gif', workersPath: ''});
  //capturer.start();

  //start the game loop
  loop();

},

stopCapture = (e) => {
  //capturer.stop();
  //capturer.save();
}

loop = () => {
  stats.begin();

  //game timer
  let now = new Date().getTime();
  dt = Math.min(1, (now - last) / 1000);
  t += dt;



  //draw current state to buffer
  states[state].render();

  //update
  states[state].step(dt);

  last = now;




  //draw buffer to screen
  render();

  //GIF capture
  //capturer.capture(C);

  stats.end();
  requestAnimationFrame(loop);
}

soundInit = () => {

  sounds = {};
  //if(audioCtx){audioCtx.close()};
  window.AudioContext = window.AudioContext || window.webkitAudioContext;
  if(!audioCtx) audioCtx = new AudioContext;


  let soundGen = new sonantx.SoundGenerator(assets.laser);
  soundGen.createAudioBuffer(147, function(buffer) {
    sounds.laser = buffer;
  });



}

eventInit = () => {
  //initialize keypress event listeners
  window.addEventListener('keyup', function (event) {
    Key.onKeyup(event);
  }, false);
  window.addEventListener('mousedown', function (event){
    stopCapture(event);
  }, false);
  window.addEventListener('keydown', function (event) {
    Key.onKeydown(event);
  }, false);
  window.addEventListener('blur', function (event) {
    paused = true;
  }, false);
  window.addEventListener('focus', function (event) {
    paused = false;
  }, false);
}

states.gameover = {

    step: function(dt) {

        if(Key.isDown(Key.r)){
          state = 'menu';
        }

    },

    render: function(dt) {
      renderTarget = 0x0;
      clear(0);

      //fr(0,0,64,64,2);

      text({
              x: 256,
              y: 80 + Math.sin(t*2.5)*15,
              text: 'GAME OVER',
              hspacing: 8 + Math.cos(t*2.9)*4,
              vspacing: 15 + Math.sin(t*3.5)*5,
              halign: 'center',
              valign: 'top',
              scale: 9,
              snap: 1,
              render: 1,
              color: 27,
          });


    },



};

states.menu = {

    step: function(dt) {

        //game update
        if(Key.isDown(Key.p)){
          state = 'game';
        }

    },

    render: function(dt) {

      renderTarget = 0x0;

      clear(0);

      let s = 256;
      let i = t/3;
      for(let y = -128; y < 128; y += 1 ){
        for(let x = -256; x < 256; x += 2 ){
          pset(s+x+256*Math.cos( (y/128+i)*4 )+y, s+y+128*Math.sin( (x/256+i)*4 )+x, x/8%32)
        }
      }

      text({
              x: 256,
            y: 40 + Math.sin(t*2.5)*15,
              text: 'PROTOGAME',
              hspacing: 8 + Math.cos(t*2.9)*4,
              vspacing: 15 + Math.sin(t*3.5)*5,
              halign: 'center',
              valign: 'top',
              scale: 9,
              snap: 1,
              render: 1,
              color: 21,
          });

      text({
              x: 256,
              y: 230,
              text: "PRESS P TO CONTINUE",
              hspacing: 2,
              vspacing: 2,
              halign: 'center',
              valign: 'top',
              scale: 1,
              snap: 1,
              render: 1,
              color: 21,
          });
        //draw stuff here.

    },



};

states.game = {


  step(dt) {

    player.update(dt);

    //----hacky sound test
    if(Key.justReleased(Key.SPACE)){
      songTrigger = true
    }
    if(songTrigger){
      playSound(sounds.laser, 1, 1, 0);
      songTrigger = false;
    }
    //---end hacky sound test

    bulletPool.use();

    Key.update();
  },

  render(dt) {

    renderTarget = 0x0;
    //background dot waves
    clear(1);
    let s = 256;
    let i = t/3;
    for(let y = -128; y < 128; y += 1 ){
      for(let x = -256; x < 256; x += 2 ){
        pset(s+x+256*Math.cos( (y/128+i)*4 )+y, s+y+128*Math.sin( (x/256+i)*4 )+x, x/8%32)
      }
    }

    //foreground octopus thing
    renderTarget = 0x40000; //rendering to different area, for collision checks and composite effects.
    clear(0);
    for(var a = 0; a < 2 * Math.PI; a+= 0.7){
      for(var r = 20; r < 200; r += 9){
        let v = a + .4 * Math.sin(a*8-r/20+t*1.7);
        fillCircle((256+r*Math.cos(v)), 80+r*Math.sin(v), (10-r/12)|0, 10+(r/9%32)|0 );

      }
    }
    renderTarget = 0x0;
    renderSource = 0x40000;
    spr(0,0,512,256);
    player.draw();

    renderTarget = 0;
    text({
            x: 256,
            y: 20,
            text: "YOU CANT DEFEAT HIM, THIS IS JUST A PIXEL-PERFECT COLLISION\nAND MULTI-LAYER DRAWING DEMO\nARROW KEYS OR WASD AND SPACE TO SHOOT",
            hspacing: 2,
            vspacing: 2,
            halign: 'center',
            valign: 'top',
            scale: 1,
            snap: 1,
            render: 1,
            color: 21,
        });


  },

};

function drawExplode(x,y){
  fillCircle(x,y, 20, 21);
}

function Particle() {

  this.inUse = false;

  this.init = function(){
    this.x = -500;
    this.y = -500;
    this.dead = true;
    this.xvel = 0;
    this.yvel = 1;
    this.life = 1;
  }

  Particle.prototype.spawn = function(opt) {
    this.x = opt.x;
    this.y = opt.y;
    this.xvel = opt.xvel;
    this.yvel = opt.yvel;
    this.inUse = true;
    this.life = opt.life || 1;
    this.remaining = opt.life || 1;
    this.radius = opt.radius || 1;
    this.color = opt.color || 21;
    this.dead = false;
  }

  Particle.prototype.use = function(dt){
    if(this.dead) {
      return true;
    }
    else {
      this.remaining -= dt;
      this.x += dt * this.xvel;
      this.y += dt * this.yvel;
      this.draw();
      //console.log('bullet used/updated');
        if(this.remaining <= 0) {
          this.dead = true;
          return true;
        }
        if(this.y < 0){
          this.dead = true;
        }
        if(this.x >= 0 && this.x <= WIDTH && this.y >=0 && this.y <= 256){  //is it on screen?
          if(ram[0x40000 + ( (this.y|0) * WIDTH + (this.x|0) )] > 0) {  //is it overlapping something drawn into the collision buffer?

            this.dead = true;
            drawExplode(this.x, this.y);
          }
        }

    }
    return false;
  }


  Particle.prototype.clear = function(){
    this.x = -500;
    this.y = -500;
    this.dead = true;
    this.xvel = 0;
    this.yvel = 0;
    this.life = 1;
    this.inUse = false;
  }

  Particle.prototype.draw = function(){
    circle(this.x, this.y, 0|Math.random()*4, 21);
  }


}

/**
 * Created by ryan on 9/4/16.
 */
/**
 * The Object Pool. Unused objects are grabbed from the back of
 * the array and pushed to the front of the array. When using an
 * object, if the object is ready to be removed, it splices the
 * array and pushes the object to the back to be reused.
 */
function Pool(poolSize, objClass) {
    var size = poolSize; // Max objects allowed in the pool
    var pool = [];
    /*
     * Populates the pool array with objects
     */
    this.init = function() {
        for (var i = 0; i < size; i++) {
            // Initialize the objects
            var obj = new objClass();
            obj.init();
            pool[i] = obj;
        }
    };
    /*
     * Grabs the last item in the list and initializes it and
     * pushes it to the front of the array.
     */
    this.get = function(opt) {
        // If the last item in the array is in use, the pool is full
        if(!pool[size - 1].inUse) {
            pool[size - 1].spawn(opt);
            pool.unshift(pool.pop());
            return pool[size -1];
        }
    };

    this.getPool = function() {
        return pool;
    }
    /*
     * Uses any alive objects in the pool. If the call returns true,
     * the object is ready to be cleared and reused.
     */
    this.use = function() {
        for (var i = 0; i < size; i++) {
            //.log('pool use iteration');
            // Only use objects that are currently in use
            if (pool[i].inUse) {
                if (pool[i].use(dt)) {
                    pool[i].clear();
                    pool.push((pool.splice(i,1))[0]);
                }
            } else {
                // The first occurrence of an unused item we can
                // break looping over the objects.
                break;
            }
        }
    };
};

//---------SONANT-X---------
/*
// Sonant-X
//
// Copyright (c) 2014 Nicolas Vanhoren
//
// Sonant-X is a fork of js-sonant by Marcus Geelnard and Jake Taylor. It is
// still published using the same license (zlib license, see below).
//
// Copyright (c) 2011 Marcus Geelnard
// Copyright (c) 2008-2009 Jake Taylor
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
//    distribution.
*/

var sonantx = {};

var WAVE_SPS = 44100;                    // Samples per second
var WAVE_CHAN = 2;                       // Channels
var MAX_TIME = 33; // maximum time, in millis, that the generator can use consecutively

var audioCtx = null;

// Oscillators
function osc_sin(value){
    return Math.sin(value * 6.283184);
}

function osc_square(value){
    if(osc_sin(value) < 0) return -1;
    return 1;
}

function osc_saw(value){
    return (value % 1) - 0.5;
}

function osc_tri(value){
    var v2 = (value % 1) * 4;
    if(v2 < 2) return v2 - 1;
    return 3 - v2;
}

// Array of oscillator functions
var oscillators = [
    osc_sin,
    osc_square,
    osc_saw,
    osc_tri
];

function getnotefreq(n){
    return 0.00390625 * Math.pow(1.059463094, n - 128);
}

function genBuffer(waveSize, callBack) {
    setTimeout(function() {
        // Create the channel work buffer
        var buf = new Uint8Array(waveSize * WAVE_CHAN * 2);
        var b = buf.length - 2;
        var iterate = function() {
            var begin = new Date();
            var count = 0;
            while(b >= 0)
            {
                buf[b] = 0;
                buf[b + 1] = 128;
                b -= 2;
                count += 1;
                if (count % 1000 === 0 && (new Date() - begin) > MAX_TIME) {
                    setTimeout(iterate, 0);
                    return;
                }
            }
            setTimeout(function() {callBack(buf);}, 0);
        };
        setTimeout(iterate, 0);
    }, 0);
}

function applyDelay(chnBuf, waveSamples, instr, rowLen, callBack) {
    var p1 = (instr.fx_delay_time * rowLen) >> 1;
    var t1 = instr.fx_delay_amt / 255;

    var n1 = 0;
    var iterate = function() {
        var beginning = new Date();
        var count = 0;
        while(n1 < waveSamples - p1)
        {
            var b1 = 4 * n1;
            var l = 4 * (n1 + p1);

            // Left channel = left + right[-p1] * t1
            var x1 = chnBuf[l] + (chnBuf[l+1] << 8) +
                (chnBuf[b1+2] + (chnBuf[b1+3] << 8) - 32768) * t1;
            chnBuf[l] = x1 & 255;
            chnBuf[l+1] = (x1 >> 8) & 255;

            // Right channel = right + left[-p1] * t1
            x1 = chnBuf[l+2] + (chnBuf[l+3] << 8) +
                (chnBuf[b1] + (chnBuf[b1+1] << 8) - 32768) * t1;
            chnBuf[l+2] = x1 & 255;
            chnBuf[l+3] = (x1 >> 8) & 255;
            ++n1;
            count += 1;
            if (count % 1000 === 0 && (new Date() - beginning) > MAX_TIME) {
                setTimeout(iterate, 0);
                return;
            }
        }
        setTimeout(callBack, 0);
    };
    setTimeout(iterate, 0);
}

sonantx.AudioGenerator = function(mixBuf) {
    this.mixBuf = mixBuf;
    this.waveSize = mixBuf.length / WAVE_CHAN / 2;
};

sonantx.AudioGenerator.prototype.getAudioBuffer = function(callBack) {
    if (audioCtx === null)
        audioCtx = new AudioContext();
    var mixBuf = this.mixBuf;
    var waveSize = this.waveSize;

    var waveBytes = waveSize * WAVE_CHAN * 2;
    var buffer = audioCtx.createBuffer(WAVE_CHAN, this.waveSize, WAVE_SPS); // Create Mono Source Buffer from Raw Binary
    var lchan = buffer.getChannelData(0);
    var rchan = buffer.getChannelData(1);
    var b = 0;
    var iterate = function() {
        var beginning = new Date();
        var count = 0;
        while (b < (waveBytes / 2)) {
            var y = 4 * (mixBuf[b * 4] + (mixBuf[(b * 4) + 1] << 8) - 32768);
            y = y < -32768 ? -32768 : (y > 32767 ? 32767 : y);
            lchan[b] = y / 32768;
            y = 4 * (mixBuf[(b * 4) + 2] + (mixBuf[(b * 4) + 3] << 8) - 32768);
            y = y < -32768 ? -32768 : (y > 32767 ? 32767 : y);
            rchan[b] = y / 32768;
            b += 1;
            count += 1;
            if (count % 1000 === 0 && new Date() - beginning > MAX_TIME) {
                setTimeout(iterate, 0);
                return;
            }
        }
        setTimeout(function() {callBack(buffer);}, 0);
    };
    setTimeout(iterate, 0);
};

sonantx.SoundGenerator = function(instr, rowLen) {
    this.instr = instr;
    this.rowLen = rowLen || 5605;

    this.osc_lfo = oscillators[instr.lfo_waveform];
    this.osc1 = oscillators[instr.osc1_waveform];
    this.osc2 = oscillators[instr.osc2_waveform];
    this.attack = instr.env_attack;
    this.sustain = instr.env_sustain;
    this.release = instr.env_release;
    this.panFreq = Math.pow(2, instr.fx_pan_freq - 8) / this.rowLen;
    this.lfoFreq = Math.pow(2, instr.lfo_freq - 8) / this.rowLen;
};

sonantx.SoundGenerator.prototype.genSound = function(n, chnBuf, currentpos) {
    var marker = new Date();
    var c1 = 0;
    var c2 = 0;

    // Precalculate frequencues
    var o1t = getnotefreq(n + (this.instr.osc1_oct - 8) * 12 + this.instr.osc1_det) * (1 + 0.0008 * this.instr.osc1_detune);
    var o2t = getnotefreq(n + (this.instr.osc2_oct - 8) * 12 + this.instr.osc2_det) * (1 + 0.0008 * this.instr.osc2_detune);

    // State variable init
    var q = this.instr.fx_resonance / 255;
    var low = 0;
    var band = 0;
    for (var j = this.attack + this.sustain + this.release - 1; j >= 0; --j)
    {
        var k = j + currentpos;

        // LFO
        var lfor = this.osc_lfo(k * this.lfoFreq) * this.instr.lfo_amt / 512 + 0.5;

        // Envelope
        var e = 1;
        if(j < this.attack)
            e = j / this.attack;
        else if(j >= this.attack + this.sustain)
            e -= (j - this.attack - this.sustain) / this.release;

        // Oscillator 1
        var t = o1t;
        if(this.instr.lfo_osc1_freq) t += lfor;
        if(this.instr.osc1_xenv) t *= e * e;
        c1 += t;
        var rsample = this.osc1(c1) * this.instr.osc1_vol;

        // Oscillator 2
        t = o2t;
        if(this.instr.osc2_xenv) t *= e * e;
        c2 += t;
        rsample += this.osc2(c2) * this.instr.osc2_vol;

        // Noise oscillator
        if(this.instr.noise_fader) rsample += (2*Math.random()-1) * this.instr.noise_fader * e;

        rsample *= e / 255;

        // State variable filter
        var f = this.instr.fx_freq;
        if(this.instr.lfo_fx_freq) f *= lfor;
        f = 1.5 * Math.sin(f * 3.141592 / WAVE_SPS);
        low += f * band;
        var high = q * (rsample - band) - low;
        band += f * high;
        switch(this.instr.fx_filter)
        {
            case 1: // Hipass
                rsample = high;
                break;
            case 2: // Lopass
                rsample = low;
                break;
            case 3: // Bandpass
                rsample = band;
                break;
            case 4: // Notch
                rsample = low + high;
                break;
            default:
        }

        // Panning & master volume
        t = osc_sin(k * this.panFreq) * this.instr.fx_pan_amt / 512 + 0.5;
        rsample *= 39 * this.instr.env_master;

        // Add to 16-bit channel buffer
        k = k * 4;
        if (k + 3 < chnBuf.length) {
            var x = chnBuf[k] + (chnBuf[k+1] << 8) + rsample * (1 - t);
            chnBuf[k] = x & 255;
            chnBuf[k+1] = (x >> 8) & 255;
            x = chnBuf[k+2] + (chnBuf[k+3] << 8) + rsample * t;
            chnBuf[k+2] = x & 255;
            chnBuf[k+3] = (x >> 8) & 255;
        }
    }
};

sonantx.SoundGenerator.prototype.getAudioGenerator = function(n, callBack) {
    var bufferSize = (this.attack + this.sustain + this.release - 1) + (32 * this.rowLen);
    var self = this;
    genBuffer(bufferSize, function(buffer) {
        self.genSound(n, buffer, 0);
        applyDelay(buffer, bufferSize, self.instr, self.rowLen, function() {
            callBack(new sonantx.AudioGenerator(buffer));
        });
    });
};

// sonantx.SoundGenerator.prototype.createAudio = function(n, callBack) {
//     this.getAudioGenerator(n, function(ag) {
//         callBack(ag.getAudio());
//     });
// };

sonantx.SoundGenerator.prototype.createAudioBuffer = function(n, callBack) {
    this.getAudioGenerator(n, function(ag) {
        ag.getAudioBuffer(callBack);
    });
};

sonantx.MusicGenerator = function(song) {
    this.song = song;
    // Wave data configuration
    this.waveSize = WAVE_SPS * song.songLen; // Total song size (in samples)
};
sonantx.MusicGenerator.prototype.generateTrack = function (instr, mixBuf, callBack) {
    var self = this;
    genBuffer(this.waveSize, function(chnBuf) {
        // Preload/precalc some properties/expressions (for improved performance)
        var waveSamples = self.waveSize,
            waveBytes = self.waveSize * WAVE_CHAN * 2,
            rowLen = self.song.rowLen,
            endPattern = self.song.endPattern,
            soundGen = new sonantx.SoundGenerator(instr, rowLen);

        var currentpos = 0;
        var p = 0;
        var row = 0;
        var recordSounds = function() {
            var beginning = new Date();
            while (true) {
                if (row === 32) {
                    row = 0;
                    p += 1;
                    continue;
                }
                if (p === endPattern - 1) {
                    setTimeout(delay, 0);
                    return;
                }
                var cp = instr.p[p];
                if (cp) {
                    var n = instr.c[cp - 1].n[row];
                    if (n) {
                        soundGen.genSound(n, chnBuf, currentpos);
                    }
                }
                currentpos += rowLen;
                row += 1;
                if (new Date() - beginning > MAX_TIME) {
                    setTimeout(recordSounds, 0);
                    return;
                }
            }
        };

        var delay = function() {
            applyDelay(chnBuf, waveSamples, instr, rowLen, finalize);
        };

        var b2 = 0;
        var finalize = function() {
            var beginning = new Date();
            var count = 0;
            // Add to mix buffer
            while(b2 < waveBytes)
            {
                var x2 = mixBuf[b2] + (mixBuf[b2+1] << 8) + chnBuf[b2] + (chnBuf[b2+1] << 8) - 32768;
                mixBuf[b2] = x2 & 255;
                mixBuf[b2+1] = (x2 >> 8) & 255;
                b2 += 2;
                count += 1;
                if (count % 1000 === 0 && (new Date() - beginning) > MAX_TIME) {
                    setTimeout(finalize, 0);
                    return;
                }
            }
            setTimeout(callBack, 0);
        };
        setTimeout(recordSounds, 0);
    });
};
sonantx.MusicGenerator.prototype.getAudioGenerator = function(callBack) {
    var self = this;
    genBuffer(this.waveSize, function(mixBuf) {
        var t = 0;
        var recu = function() {
            if (t < self.song.songData.length) {
                t += 1;
                self.generateTrack(self.song.songData[t - 1], mixBuf, recu);
            } else {
                callBack(new sonantx.AudioGenerator(mixBuf));
            }
        };
        recu();
    });
};
// sonantx.MusicGenerator.prototype.createAudio = function(callBack) {
//     this.getAudioGenerator(function(ag) {
//         callBack(ag.getAudio());
//     });
// };
sonantx.MusicGenerator.prototype.createAudioBuffer = function(callBack) {
    this.getAudioGenerator(function(ag) {
        ag.getAudioBuffer(callBack);
    });
};

//---------END SONANT-X-----

// stats.js - http://github.com/mrdoob/stats.js
var Stats=function(){function h(a){c.appendChild(a.dom);return a}function k(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault();k(++l%c.children.length)},!1);var g=(performance||Date).now(),e=g,a=0,r=h(new Stats.Panel("FPS","#0ff","#002")),f=h(new Stats.Panel("MS","#0f0","#020"));
    if(self.performance&&self.performance.memory)var t=h(new Stats.Panel("MB","#f08","#201"));k(0);return{REVISION:16,dom:c,addPanel:h,showPanel:k,begin:function(){g=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();f.update(c-g,200);if(c>e+1E3&&(r.update(1E3*a/(c-e),100),e=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){g=this.end()},domElement:c,setMode:k}};
Stats.Panel=function(h,k,l){var c=Infinity,g=0,e=Math.round,a=e(window.devicePixelRatio||1),r=80*a,f=48*a,t=3*a,u=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=f;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,f);b.fillStyle=k;b.fillText(h,t,u);b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(f,
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         v){c=Math.min(c,f);g=Math.max(g,f);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=k;b.fillText(e(f)+" "+h+" ("+e(c)+"-"+e(g)+")",t,u);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,e((1-f/v)*p))}}};"object"===typeof module&&(module.exports=Stats);

    Key = {

        _pressed: {},
        _released: {},

        LEFT: 37,
        UP: 38,
        RIGHT: 39,
        DOWN: 40,
        SPACE: 32,
        a: 65,
        w: 87,
        s: 83,
        d: 68,
        z: 90,
        x: 88,
        f: 70,
        p: 80,
        r: 82,

        isDown(keyCode) {
            return this._pressed[keyCode];
        },

        justReleased(keyCode) {
            return this._released[keyCode];
        },

        onKeydown(event) {
            this._pressed[event.keyCode] = true;
        },

        onKeyup(event) {
            this._released[event.keyCode] = true;
            delete this._pressed[event.keyCode];

        },

        update() {
            this._released = {};
        }
    };


function textLine(opt) {

	var textLength = opt.text.length,
		size = 5;

	for (var i = 0; i < textLength; i++) {

		var letter = [];
		letter = getCharacter( opt.text.charAt(i) );
		//console.log(letter);
		//console.log(assets.letters[ opt.text.charAt(i)]);

		for (var y = 0; y < size; y++) {
			for (var x = 0; x < size; x++) {
				//if (letter[y][x] == 1) {
				if (letter[y*size+x] == 1){
					if(opt.scale == 1){
						pset(
							opt.x + ( x * opt.scale ) + ( ( size * opt.scale ) + opt.hspacing ) * i,
							opt.y + (y * opt.scale),
							opt.color
						);
					}

					else {
						fr(
						opt.x + ( x * opt.scale ) + ( ( size * opt.scale ) + opt.hspacing ) * i,
						opt.y + (y * opt.scale),
						opt.scale,
						opt.scale,
						opt.color);
					}

				} //end draw routine
			}  //end x loop
		}  //end y loop
	}  //end text loop
}  //end textLine()

function text(opt) {
	var size = 5,
	letterSize = size * opt.scale,
	lines = opt.text.split('\n'),
	linesCopy = lines.slice(0),
	lineCount = lines.length,
	longestLine = linesCopy.sort(function (a, b) {
		return b.length - a.length;
	})[0],
	textWidth = ( longestLine.length * letterSize ) + ( ( longestLine.length - 1 ) * opt.hspacing ),
	textHeight = ( lineCount * letterSize ) + ( ( lineCount - 1 ) * opt.vspacing );

	if(!opt.halign)opt.halign = 'left';
	if(!opt.valign)opt.valign = 'bottom';

	var sx = opt.x,
		sy = opt.y,
		ex = opt.x + textWidth,
		ey = opt.y + textHeight;

	if (opt.halign == 'center') {
		sx = opt.x - textWidth / 2;
		ex = opt.x + textWidth / 2;
	} else if (opt.halign == 'right') {
		sx = opt.x - textWidth;
		ex = opt.x;
	}

	if (opt.valign == 'center') {
		sy = opt.y - textHeight / 2;
		ey = opt.y + textHeight / 2;
	} else if (opt.valign == 'bottom') {
		sy = opt.y - textHeight;
		ey = opt.y;
	}

	var cx = sx + textWidth / 2,
		cy = sy + textHeight / 2;

	if (opt.render) {
		for (var i = 0; i < lineCount; i++) {
			var line = lines[i],
				lineWidth = ( line.length * letterSize ) + ( ( line.length - 1 ) * opt.hspacing ),
				x = opt.x,
				y = opt.y + ( letterSize + opt.vspacing ) * i;

			if (opt.halign == 'center') {
				x = opt.x - lineWidth / 2;
			} else if (opt.halign == 'right') {
				x = opt.x - lineWidth;
			}

			if (opt.valign == 'center') {
				y = y - textHeight / 2;
			} else if (opt.valign == 'bottom') {
				y = y - textHeight;
			}

			if (opt.snap) {
				x = Math.floor(x);
				y = Math.floor(y);
			}

			textLine({
				x: x,
				y: y,
				text: line,
				hspacing: opt.hspacing || 0,
				scale: opt.scale || 1,
				color: opt.color

			});
		}
	}

	return {
		sx: sx,
		sy: sy,
		cx: cx,
		cy: cy,
		ex: ex,
		ey: ey,
		width: textWidth,
		height: textHeight
	}
}

function getCharacter(char){
	index = fontString.indexOf(char);
	return fontBitmap.substring(index * 25, index*25+25).split('') ;
}

// function flattenArray(){
// 	var bigString = "";
// 	bin = assets.font.bin;
// 	for(var i = 0; i < assets.font.string.length; i++){
// 		for(var j = 0; j < 5; j++){
// 			bigString += bin[j][0].substring(i * 5, i*5+5);
// 		}
// 	}
// 	console.log(bigString);
// 	return bigString;
// }

// function binToAscii(b,a,i){
// 	a="";
// 	for(i=0;i<b.length;i+=7)a+=String.fromCharCode(parseInt(b.substr(i,7),2));
// 	return a
// 	}


//function asciiToBin(s,b,i){b="";for(i in s)b+=(1e7+s.charCodeAt(i).toString(2)).slice(-7);return b}

player = {
  // x: 0,
  // y: 0,
  // radius: 12,
  // xvel: 0,
  // yvel: 0,
  // speed: 6,
  // drag: .97,

  bullet: {
    x: 0, y:0, xvel: 0, yvel: 0
  },

  init (){
    this.x = 64;
    this.y =  230;
    this.radius = 12;
    this.xvel = 0;
    this.yvel = 0;
    this.xspeed = 400;
    this.yspeed = 400;
    this.drag = .6;
  },

  update (dt) {
    this.bullet.x = player.x;
    this.bullet.y = player.y;
    this.xvel *= player.drag;
    this.yvel *= player.drag;
    let xIntegrate = dt * player.xvel;
    let yIntegrate = dt * player.yvel;

    player.x += xIntegrate;
    player.y += yIntegrate;

    //player movement
    if (Key.isDown(Key.d) || Key.isDown(Key.RIGHT)) {
        player.xvel =  player.xspeed;
    }
    if (Key.isDown(Key.a) || Key.isDown(Key.LEFT)){
        player.xvel =  - player.xspeed;
    }
    if(Key.isDown(Key.w) || Key.isDown(Key.UP)){
      player.yvel = -player.yspeed;
    }
    if(Key.isDown(Key.s) || Key.isDown(Key.DOWN)) {
      player.yvel = player.yspeed;
    }

    if(Key.isDown(Key.SPACE || Key.isDown(Key.z))){
      //player.bullet.xvel = E.player.xvel;
      player.bullet.yvel = -350;
      bulletPool.get(player.bullet);
    }

    //world wrap for player
    if(player.x > WIDTH){
      player.x = 0;
    }
    if(player.x < 0){
      player.x = WIDTH;
    }
    if(player.y > HEIGHT){
      player.y = 0;
    }
    if(player.y < 0){
      state = 'gameover';
      player.y = HEIGHT;
    }
    //end world wrap for player


  },

  draw (dt) {

    // let degrees = (360/256) * E.player.x * 0.0174533;
    // let radius = (E.player.y / 2);

    // let playerDrawPoint = E.util.toPolarScreen({x:E.player.x, y:E.player.y});
    //
    // let distFromCenter = E.util.dist(playerDrawPoint.x+128, playerDrawPoint.y+128, 128,128);
    //
    // let playerSizeFactor = E.util.norm(distFromCenter, 0, 128);

    //E.renderTarget = E.screen;
    //E.gfx.fillCircle(playerDrawPoint.x+128, playerDrawPoint.y+128, E.player.radius * playerSizeFactor, 21);

    fillCircle(this.x, this.y, this.radius, 21);



  },

}

window.onload = init();
}
()
)

            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console