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

              
                <body style=margin:0;background:#111;color:#fff>
<center>
<canvas id=a width=1280 height=720 style=background:#fff></canvas>
<h1>バタフライコ (Batafuraiko)</h1>
<i>Invasive species, the butterflys at warp speed, 
dancing with bullets.</i><br>
<br><b>
<a href=https://js1024.fun/demos/2021#12>A JS1024 game by KilledByAPixel</a></b><br>
<h1>How to Play</h1>
Use mouse to fly.
You have 1 life to survive 9 waves.
Press R to reset.<br>
Chrome recommended, Firefox works but is slow.
</center>
              
            
!

CSS

              
                
              
            
!

JS

              
                'use strict';

// shim creates 2d context
const c = a.getContext("2d");

// constants (will be auto replace in minified)
const WIDTH = 1280;
const HEIGHT = 720;

const TYPE_Explosion        = -1;
const TYPE_Bullet           = 0;
const TYPE_Player           = 1;
const TYPE_Enemy            = 3;
const TYPE_Enemy_Pawn       = 3;
const TYPE_Enemy_Scout      = 4;
const TYPE_Enemy_Fighter    = 5;
const TYPE_Enemy_Battleship = 6;
const TYPE_Enemy_Mothership = 7;
const TYPE_Enemy_Cthulhu    = 8;

// variables (remove from minified)
let i, frame, seed, globalSeed, player, spawnPool, spawnWait, wave, enemyCount, spawnCount, objects, Rand, MakeObject;

// get seeded random value between 0 and max
Rand = (max=1)=> Math.sin(++seed)**2 * 1e5 % 1 * max;

// spawn object
MakeObject =
(
    t,        // type
    x, y,     // position
    r,        // radius
    v, w,     // velocity
    e,        // team (0==player, 1==enemy)
    h=1,      // hit points
)=> objects.push({t, x, y, r, v, w, e, h, s:h});

// init and spawn player
objects = [];
MakeObject(TYPE_Player, 0, -72, 15, 0, 0, 0); // create player

// init and spawn player
let Reset=_=>
{
    objects = [];
    MakeObject(TYPE_Player, 0, -72, 15, 0, 0, 0); // create player
    player = objects[frame = globalSeed = wave = spawnCount = enemyCount = 0];
}
Reset();
onkeydown = e=>
{
    if (e.keyCode==82)
        Reset();
}

const gameUpdate=_=> // main game loop
{
    // update frame
    frame += enemyCount ? 1 : wave;

    // player shoot
    frame%9 || player.r && enemyCount && MakeObject(TYPE_Bullet, player.x, player.y, 19, Math.sin(frame*frame)/3, -9, 0);
    
    // create sorted list of enemy types for this wave
    seed = wave*wave;                               // set seed
    spawnPool = [];                                 // clear spawn pool
    for(i=wave; i--;                                // how many enemy types at once
        spawnPool.push(Rand(Math.min(6, wave))|0))  // max enemy type
    {
        onmousemove = e=>   // set mouse move here to save space
        {
            // movement control
            let rect = a.getBoundingClientRect();
            player.x = WIDTH * (e.x - rect.left) / rect.width;
            player.y = HEIGHT * (e.y - rect.top) / rect.height;

            player.x = Math.max(0, Math.min(WIDTH, player.x));
            player.y = Math.max(0, Math.min(HEIGHT, player.y));
        }
    }

    // spawn new enemies
    seed = wave*spawnCount;             // set seed for this enemy spawn
    spawnCount ?                        // if enemies left to spawn
        player.y < 0 ||                 // wait for player to move (not in 1k build)
        --spawnWait ||                  // and not waiting to spawn
            MakeObject(                                                                     // spawn enemy
                i = wave < 10 ? TYPE_Enemy + spawnPool[Rand(wave)|0] : TYPE_Player,         // enemy type
                Rand(WIDTH,--spawnCount), -72,                                              // x, y pos, decrement spawn count
                5*i + 4,                                                                    // radius
                Rand(2)-1,                                                                  // x speed
                Rand()+(i<TYPE_Enemy_Scout ? 3 : i>TYPE_Enemy_Scout & i<TYPE_Enemy_Mothership ? 2 : 1), // y speed
                wave < 10,                                                                  // team
                1 + (i-TYPE_Enemy)**2,                                                      // hitpoints
                spawnWait = wave < 10 ? (i - TYPE_Enemy + 2 )**2*5 : 9)                     // set spawn wait time
    :
    enemyCount || (spawnCount = ++wave+19, spawnWait = 200);      // go to next wave when no enemies are left

    // set canvas size and clear to black
    c.fillRect(enemyCount = 0, 0, a.width = WIDTH, a.height = HEIGHT); // clear enemy count

    // stars display
    for(i=seed=400; i--; c.fillStyle = `#fff`) 
        c.fillRect(Rand(WIDTH), Rand(frame)%HEIGHT, Rand(), Rand(5)); // starfield
    //c.font = '6em"';c.fillText(wave, 9, HEIGHT-9);  // HUD

    // update objects
    objects.map(o=>
    {
        // update physics
        o.x += o.v;
        o.y += o.w;

        // set global seed
        seed = ++globalSeed;

        if (o.t > TYPE_Player) // enemy update
        {
            // increment enemy count
            ++enemyCount;

            // enemy shoot
            o.t > TYPE_Enemy_Pawn &                                   // check if shooty enemy
                Rand(99) < 1 && MakeObject(                           // spawn bullet
                    o.t < TYPE_Enemy_Mothership? TYPE_Bullet : o.t-5, // projectile type
                    o.x, o.y, o.r/3,                                  // position, size
                    o.t > TYPE_Enemy_Scout? 2*Math.sin(frame) : 0,    // x speed
                    o.t &1? 2*Math.sin(frame+11) : 3,                 // y speed
                    31                                                // team ( projectile color)
                );
            
            // extra motion
            o.t & 1 ? 0 : o.v += Math.sin(frame/30)/30;
            
            // bottom wrap
            if (o.y > HEIGHT+100 & o.e < 2) // only for big enemies
            {
                // move to top
                o.y = -72;
                o.x = Rand(WIDTH);

                // restore health
                o.h = o.s;
            }
        }

        // draw object
        for(i = o.t > TYPE_Bullet ? 16 : 1; i--;
        
            // draw the object
            c.beginPath(c.fill(
                o.t > 0 ?  c.ellipse(                                       // is complex type?
                    o.x + o.r*(Rand(2)-1)*(i%2*2-1),                        // x pos with reflection
                    o.y + o.r*(Rand(2)-1),                                  // y pos
                    Rand(o.r), Rand(o.r/3),                                 // width, height
                    (Rand(9) + Math.sin((frame+o.x+o.y)/30)/3)*(i%2*2-1),   // angle & animation
                    0, 9)                                                   // full ellipse angles
                :
                // simple shape (bullet or explosion), make player have thin bullets
                c.ellipse(o.x, o.y, o.e ? o.r : 5, o.r, 0, 0, 9)
             ))
        )
        {
            // set seed for this iteration
            seed = (i/2+9|0)*o.t*9;

            // set color
            c.fillStyle = `hsl(${o.e*(160+o.t*89+o.r)+61+Rand(99)+99*o.h/o.s} 128%${50+(i>>1)*9}%`;
        }

        o.t < TYPE_Bullet ? --o.r :                    // make explosions get smaller over time
            o.e || objects.map( p=>                    // remove dead objects
                o.t != p.t &                           // not same type (prevent projectile collisons)
                Math.hypot(o.x-p.x, o.y-p.y) < p.r*2 & // overlapping
                p.e &&                                 // is enemy
                MakeObject(TYPE_Explosion, o.x, o.y, 25, 0, o.r = 0, 4, --p.h     // bullet hit effect, damage enemy
                || MakeObject(TYPE_Explosion, p.x, p.y, p.r*2, 0, p.r = 0, 2 ))   // make explosion, kill enemy
            );
        //c.beginPath(c.strokeStyle='red',c.arc(o.x,o.y,o.r,0,9),c.stroke()); // show hit box
    });

    // show title until mouse is moved (not in 1k build)
    for(i=9; player.y<0 && i--;)
    {
        c.font='8em"';c.textAlign='center';c.lineWidth=6;
        c.strokeStyle=`hsl(${frame+i*29} 100%${50}%`;c.fillStyle=`#000`;
        let x = WIDTH/2+Math.sin(frame/30+i*Math.PI+i)*i*2;
        let y = 200+Math.cos(frame/30+i*Math.PI+i*i)*i*2;
        c.fillText('バタフライコ',x,y);
        c.strokeText('バタフライコ',x,y);
    }

    // remove dead or off screen objects
    objects = objects.filter(o=>o.r > 0 & Math.hypot(o.x-WIDTH/2, o.y-HEIGHT) < WIDTH);
   // console.log(objects.length);
};

let lastTimeStamp = 0;
let timeBuffer = 0;
let FPS = 120;

const loop=timeStamp=>
{
    requestAnimationFrame(loop);
  
    // enhance, hide cursor when player is alive
    a.style.cursor = player.r ? 'none' : '';
  
    // fit canvas to window
    const aspect = WIDTH / HEIGHT;
    const width = aspect > innerWidth / innerHeight ? 
      innerWidth : innerHeight * aspect;
    a.style.width = width + 'px';
    
    // maintain framerate
    let delta = timeStamp - lastTimeStamp;
    lastTimeStamp = timeStamp;
    timeBuffer += delta;
    const frameDelta = 1e3 / FPS;
    if (timeBuffer > frameDelta*4)
        timeBuffer = frameDelta*4;
    while(timeBuffer > 0)
    {
        timeBuffer -= frameDelta;
        gameUpdate();
    }
}

loop(0);

// 1024B Batafuraiko - By Frank Force
//for(_='});u=Ze.YYtX<XWYyV,VU2*T9)H--Gm.F||Ec.fillDDRect(Cu.map(B.push(Ah=@10>I&&w()**2v(,(f/3=>1)72DStyle=`Yx))Math.min(;fG;0,,hypot(-Yr+*(2)-1	$.)*%T2-0)c.ellipse(;for=128t,x,y,r,h,w,e,gsin(v=(e=1e5*++h%1*e;w=(=uA{,i:gZ[];w(1-,15;m=u[g=l=I=t=r=0];setInterval((){g+=r?1:I;g%9EFr&&rFx,Fy,19,g*g),-9,;@I*I;n=[]InA6,I|)onmousemove=e{Fx=);Fy=V)};@I*t;t?GpEw=?3+n[I)|0]:1Gt),-,5*f+42)-1)+(4>f?3:4<f&7>f?2:,,1+-3,p=?5*-3+2:H:rE(t=++I+19,p=20;Cr=0a.widt@a.height=@400#fff`)Cg)%)5;Be{+=Yh;V+=Yw;@++l;1W&&(++r,3W&1>9H7>X?0:X-5,U,,4W?Tg):X&1?Tg+1:3,3,X&1?0:Yh+=g820<V&2>Ye&&(V=-,=,Yg=Yi0W?16:1c.beginPath(D(0W?	U	))),(H+(g++V)H:U,Ye?:5,9@/2+9|*X*9,hsl(${Ye*(160+89*X+)+61+9H+99*Yg/Yi} %${50+9*>>}%`;0>X?G:YeEB$X!=t&xU-y)<Tr&e-1,U,25=4,GgEw(-1,x,y,Trr=2)Zu.filter(e0<&0>64V-)},8)';G=/[-@-HT-Z]/.exec(_);)with(_.split(G))_=join(shift());eval(_)
              
            
!
999px

Console