Pen Settings



CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource


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


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.


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.


                <!-- where our canvas goes -->
<div id="game-wrapper"></div>

<!-- where our music comes from -->
<!-- DKC2 - Bramble Blast  -->
<iframe width="560" height="315" src="" frameborder="0" allowfullscreen style="display:none"></iframe>




                // This is a long one. I hope this over-commenting helps. Let's do this!

// Let's put our settings up top so we can change them easily
let settings = {
  spotlightRadius: 400,
  boxCount: 50,
  moveSpeed: 1,
  turboSpeed: 5,

// Let's define our states, there's only one in this game at the moment
function state(s){
  // we call our init state all the way down the bottom of our code
  if (s==="init"){
    // let's create an instance of our class Application
    const app = new Application;

    // Add our UI
    let wrapper = document.querySelector('#game-wrapper');
    // update light position
      app.universe.light.position.x = e.clientX*window.devicePixelRatio;
      app.universe.light.position.y = e.clientY*window.devicePixelRatio;
    // engage turbo when our mouse is down
    wrapper.addEventListener('mousedown',() => {
      app.universe.speed = settings.turboSpeed;
    // release turbo when mouse is up
      app.universe.speed = settings.moveSpeed;

// This is our application class. It contains our Universe which contains our Boxes and our Light
// I called them boxes, not crystals, because this project changed direction as it progressed
class Application {
    // Our app has a width and a height
    // wWe don't know their size yet but we can figure them out using the resize function below
    this.width = null;
    this.height = null;

    let wrapper = document.querySelector('#game-wrapper');
    // Let's create our canvas that the game will be rendered on
    this.canvas = document.createElement('canvas');
    // and put it inside of our wrapper
    // create a context for it that we will render into, it's a 2d sim
    this.context = this.canvas.getContext('2d');

    // We'll also need a masking canvas to hide crystals ourside of the lit area
    // It's the black outside of our visible ring, we'll call it an overlay
    this.canvasOverlay = document.createElement('canvas');
    this.contextOverlay = this.canvasOverlay.getContext('2d');

    // Let's resize our canvas
    // and set up a listener which will resize it again if the window size changes
    window.addEventListener('resize', () => this.resize(), false);

    // if you haven't come across this "() => foo()":
    // It's just a condensed way of writing "function() {foo()}"
    // read here:

    // Let's add our Universe class to our app and pass through the width and height values
    this.universe = new Universe(this.width,this.height);

    // and start our render function

    // Canvases need to be cleared each frame or else what you draw will just layer on top
    // So let's clear the whole canvas

    // Let's fill in the "lit" area around our mouse with a nice gradien so the light looks like it fades away
    // First let's create our gradient
    let gradient=this.context.createRadialGradient(this.universe.light.position.x,this.universe.light.position.y,0.9*settings.spotlightRadius,this.universe.light.position.x,this.universe.light.position.y,0);
    // ... and pass through our hex colors

    // Now let's add this gradient to our canvas context
    this.context.fillStyle = gradient;

    // Now let's draw the boxes from our universe
    // They exist in the universe, but everything is rendered in our app's render step
    let boxes = this.universe.boxArray;
    // create a for loop that goes through our box array
    for (var i=0; i<boxes.length;i++){
      //  and pulls out each box one at a time
      let box = boxes[i];

      // Let's draw this crystal
      // -----------------------
      // First let's get it's color
      this.context.fillStyle = box.color;
      this.context.strokeStyle = box.color;
      // For each side of the crystal we'll have to project that side into the distance:
      // Notice that each point on a crystal casts a "shadow" that extends away from where a mouse is
      // So for each point on this box let's do that
      for (let i = 0;i<box.shadowCorners.length;i++){
        // shade by section between this point and the next point and then the shadow
        // and loop back to the start if we're at our final point (to close the shape)
        let j = (i<box.shadowCorners.length-1) ? i+1:0

        // Let's project the side of our crystal off into the distance by creating a polygon
        // this is tricky to describe but maybe this will help

        // close out path, fill and stroke and 

        // We repeat this process for every side of every crystal :o computers are so cool

      // Okay but we've still got to draw the happy box over the top of this mess
      // fortunately this is just one shape
      for (let i = 1;i<box.sides;i++){

    // Let's paint a little happy circle in where our mouse is
    // Remember, this is your universe, you don't have to do this
    // Just make it up as you go... (RIP Bob Ross)

    // Now let's draw our overlay
    // First let's clear the old one

    // We want a transparent circle centered around our mouse
    // rimmed with the color that our light fades out to

    // Try commenting out these lines to see it without the overlay
    this.contextOverlay.fillStyle = gradient;

    // Now update the position of everything in our universe

    // And request another render frame


    // Set our app width and height to that of the window
    this.width = window.innerWidth;
    this.height = window.innerHeight;

    // And resize our wrapper to these dimensions
    document.querySelector('#game-wrapper').style.width = this.width+'px';
    document.querySelector('#game-wrapper').style.height = this.height+'px';

    // Then get the size for our canvas based off the pixel density of the screen
    this.width *= window.devicePixelRatio;
    this.height *= window.devicePixelRatio;

    // and resize our canvases
    this.canvas.width = this.width;
    this.canvas.height = this.height;
    this.canvasOverlay.width = this.width;
    this.canvasOverlay.height = this.height;

    // regenerate our boxes
    if (this.universe) this.universe.generateBoxes();

    // this will mix the colors of the crystals as they are laid over the top of one another
    this.context.globalCompositeOperation = 'screen';

    // And define our center pixel = {
      x: this.width/2,
      y: this.height/2

// Our Universe class contains our light and our Boxes (crystals)
class Universe{
    // It inherits the width and height of our app
    this.width = width;
    this.height = height;
    this.speed = settings.moveSpeed;

    // Let's store all of the boxes in our universe in an array for nice access
    this.boxArray = [];

    // generate our Boxes

    // And also add the light which hangs out on our mouse
    this.light = new Light(width*.75,height*.35);

    // Clear array if there is one
    this.boxArray = [];
    // Let's create the number of boxes we defined in our settings
    for (let i=0;i<settings.boxCount;i++){
      // create a box
      let box = new Box(this.width, this.height);
      // and add it to our array of boxes

  // This is called alongside each render frame 
    // First we calculate our change in direction based on where the mouse is 
    // the further it is from the center, the more we move in that direction
    // Our change in x
    let dx = (centerPixel.x - this.light.position.x)/100;
    // and our change in y
    let dy = (centerPixel.y - this.light.position.y)/100;

    // Now we can use this change to update our box positions and their shadows, (box)=>{

// Each box is a crystal
class Box {
    // When it's created let's give it a random position inside of the width and height of our universe
    this.position = {
      x: Math.random()*width,
      y: Math.random()*height,
    // and give it a random radius
    this.radius = Math.random()*30;
    // a random rotation
    this.rotation = Math.random();
    // a random rotation speed
    this.rotationSpeed = (Math.random()-0.5)/100;
    // a random number of sides
    // (min 3 sides max 8)
    this.sides = Math.floor(Math.random()*6+3);
    // and a random color using the getRandomColor function below
    this.color = this.getRandomColor();

    // let's create an array for each box to store the location of it's corners and the projection of those corners away from our light source
    this.corners = [];
    // and let's figure out those locations
    this.shadowCorners = this.corners;

    // update the box position
    this.position.x += dx;
    this.position.y += dy;

    // check location of our box and loop it if it's outside of canvas 
    if (this.position.x<-this.radius){
    } else if (this.position.x>centerPixel.x*2+this.radius){
      this.position.x -= centerPixel.x*2+this.radius*2;
    if (this.position.y<0-this.radius){
    } else if (this.position.y>centerPixel.y*2+this.radius){

    // rotate the box according to it's speed
    this.rotation += this.rotationSpeed;

    // Dump our old locations
    this.corners = [];
    this.shadowCorners = [];

    // and find the new corners of our box and porject them

    // We're going to draw our shapes on a circle by breaking the circle up into the number of sections that we need
    // The angle between each of our points is defined by:
    let internalAngle = Math.PI*2/this.sides;
    // see

    // Let's calculate the location of each of the corners for our polygon
    for (var i=0;i<this.sides;i++){
      // Use trig to get the location based off of our x and y
      let x = this.position.x + this.radius*Math.sin(this.rotation+i*internalAngle);
      let y = this.position.y + this.radius*Math.cos(this.rotation+i*internalAngle);
      // and push to the array

    // Our shadow corners are the projected corners of our boxes

    // for each of our box corners, let's figure out the projection
    for (var i=0;i<this.sides;i++){
      // Let's figure out the x and y of our corner relative to our light, and make our shadowCorner a point on the same line, much further away
      let dx = this.corners[i].x - light.position.x;
      let dy = this.corners[i].y - light.position.y;
      let dist = Math.sqrt(dx*dx+dy*dy);

      // extrapolate this line into the distance (relative to our starting point)
      let x = light.position.x + dx * settings.spotlightRadius / dist * 20;  // 20 times the distance of our light radius
      let y = light.position.y + dy * settings.spotlightRadius / dist * 20; 

      // lol whoop[s]
      // let x = this.corners[i].x + dx * settings.spotlightRadius / dist;
      // let y = this.corners[i].y + dy * settings.spotlightRadius / dist; 

      // and push the point to our shadowCorner array

  // function we use to get a random color
  getRandomColor() {
    let letters = '0123456789ABCDEF';
    let color = '#';
    for (let i = 0; i < 6; i++ ) {
      color += letters[Math.floor(Math.random() * 16)];
    return color;

// Our light class, pretty simple...
// I was thinking about adding multiple lights or light of different colors and creating a light class could of allowed me to do this in the future. But I didn't :P
class Light {
    this.position = {
      x: x,
      y: y,

// on load, start our initialization state
window.onload = function() {
  // followMe("init");