cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

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.

            
              <div id="stars"></div>
<div id="container"></div>
            
          
!
            
              html, body { margin: 0; padding: 0; }

body { background-color: #000 }

#container {
	z-index: 5;
	margin: auto;
	width: 100vh;
	height: 100vh;
	/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#0011ff+12,0011ff+12,d1d4ff+49,d1d4ff+49,000000+68,000000+100&1+0,1+52,0.2+76 */
	background: -moz-radial-gradient(center, ellipse cover, rgba(0,17,255,1) 0%, rgba(0,17,255,1) 12%, rgba(209,212,255,1) 49%, rgba(176,179,215,1) 52%, rgba(0,0,0,0.47) 68%, rgba(0,0,0,0.2) 76%, rgba(0,0,0,0.2) 100%); /* FF3.6-15 */
	background: -webkit-radial-gradient(center, ellipse cover, rgba(0,17,255,1) 0%,rgba(0,17,255,1) 12%,rgba(209,212,255,1) 49%,rgba(176,179,215,1) 52%,rgba(0,0,0,0.47) 68%,rgba(0,0,0,0.2) 76%,rgba(0,0,0,0.2) 100%); /* Chrome10-25,Safari5.1-6 */
	background: radial-gradient(ellipse at center, rgba(0,17,255,1) 0%,rgba(0,17,255,1) 12%,rgba(209,212,255,1) 49%,rgba(176,179,215,1) 52%,rgba(0,0,0,0.47) 68%,rgba(0,0,0,0.2) 76%,rgba(0,0,0,0.2) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#0011ff', endColorstr='#33000000',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
}

canvas {
	display: block;
	margin: 0;
	padding: 0;
}	

#stars {
	position: relative;
	z-index: -1;
}
.star {
	
	/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#ffffff+11,000000+100&1+0,0.2+99 */
background: -moz-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%, rgba(255,255,255,0.91) 11%, rgba(3,3,3,0.2) 99%, rgba(0,0,0,0.2) 100%); /* FF3.6-15 */
background: -webkit-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%,rgba(255,255,255,0.91) 11%,rgba(3,3,3,0.2) 99%,rgba(0,0,0,0.2) 100%); /* Chrome10-25,Safari5.1-6 */
background: radial-gradient(ellipse at center, rgba(255,255,255,1) 0%,rgba(255,255,255,0.91) 11%,rgba(3,3,3,0.2) 99%,rgba(0,0,0,0.2) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#33000000',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
	width: 5px;
	height: 5px;
	position: absolute;
}
            
          
!
            
              /** 
 * Newton's Cannon
 * https://en.wikipedia.org/wiki/Newton%27s_cannonball
 * @author Jeremy Heminger <contact@jeremyheminger.com>
 * @github https://github.com/061375/Newtons-Cannonball
 * @website http://jeremyheminger.com
 * 
 * @version 1.0.4
 * @date October 2018
 *
 * Credit: Vector class from here:https://codepen.io/akm2/pen/rHIsa
 *
 *
 * */
const 	G = 6.674e-11,
		WW = window.innerWidth,
		HH = window.innerHeight;

// initialize variables
var canvas, ctx, _cannon, _cannonball, _planet, _objects, c_speed = 0.1 , cspeed = 0.8, isrunnning = true, W, H, hW, hH;

// initalize listeners
window.addEventListener('load', init, false);
window.addEventListener('blur', stopLoop, false);
window.addEventListener('focus', startLoop, false);
window.addEventListener('resize', resizeHandler, false);

/** 
 * initalize
 * @function init
 * */
function init(e, b_reset) {

	W = document.getElementById('container').clientWidth;
	H = document.getElementById('container').clientHeight;
	hW = (W/2);
	hH = (H/2);
	PLANETRADIUS = (H/3);

	if (undefined === b_reset) {
		// create the canvas
		canvas = document.createElement('canvas');
		ctx = canvas.getContext('2d');
	}
	
	// instantiate our classes
	_planet = new Planet(hW,hH,PLANETRADIUS,-90);

	_cannon = new Cannon(_planet.get().x,_planet.get().y,PLANETRADIUS,-90);

	_cannonball = new Cannonball(_cannon.get().x,_cannon.get().cannon.breach.y);

	// add the objects in the order want them drawn
	// from back to front
	_objects = [
		_cannonball,
		_planet,
		_cannon
	];

	if (undefined === b_reset) {
		// add the canvas
		document.getElementById('container').appendChild(canvas);
	
		makeStars(50);
	}
	
	canvas.height = H;
	canvas.width = W;

	if (undefined === b_reset) {
		// start rendering
		this.render();
	}
}
/** 
 * make some stars
 * @method makeStars
 * @param {Number}
 * */
function makeStars(n) {

	for(let i=0; i<n; i++) {
		let s = document.createElement('div');	
			s.setAttribute('class','star');
			s.style.left = (Math.random() * WW)+'px';
			s.style.top = (Math.random() * HH)+'px';
		document.getElementById('stars').appendChild(s);
	}
}
/** 
 * just like the name implies
 * @method stopLoop
 * */
function stopLoop() {
	isrunnning = false;
	console.log('stop loop');
}
/** 
 * just like the name implies
 * @method startLoop
 * */
function startLoop() {
	isrunnning = true;
}
/** 
 * set the size of the canvas
 * @note currently this really only works once
 * @function resizeHandler
 * */
function resizeHandler() {
	canvas.height = H;
	canvas.width = W;
	_objects = [];
	_planet = null;
	_cannon = null;
	_cannonball = null;
	isrunnning = true;
	init(null, true);
}
/** 
 * clear the canvas
 * @function clear
 * */
function clear() {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
}
/** 
 * render the visible stuff
 * @method render
 * */
function render() {

	requestAnimationFrame(render);

	// if true then render 
	if(isrunnning) {
		// clear
		this.clear();
		// save
		ctx.save();
		// loop everything
		for(let i=0; i<_objects.length; i++) {
			_objects[i].loop();
			_objects[i].draw();
		}
		// restore
		ctx.restore();
	}
}
/** 
 * 
 * @function trig
 * @param {Number}
 * @param {Number}
 * @param {Number}
 * @param {Number}
 * @param {Boolean}
 * @returns {Mixed} 
 * */
function trig(x,y,r,d,array) {

	if(d<0)d+=360;
	if(d>360)d-=360;

	let a = d * Math.PI / 180;
	let xpos = r * Math.cos(a);
	let ypos = r * Math.sin(a);

    if(array) {
    	return [
    		xpos+x,
    		ypos+y
    	]
    }else{
    	return {
    		x:xpos+x,
    		y:ypos+y
    	}
    }
}
/** 
 * 
 * @function distance
 * @param {Number}
 * @param {Number}
 * @param {Number}
 * @param {Number}
 * @returns {Number}
 * */
function distance(x1,y1,x2,y2) {
	return Math.hypot(x2-x1, y2-y1);
}
/** 
 * 
 * @class
 * */
class Planet {
	/** 
	 * @param {Number}
	 * @param {Number}
	 * @param {Number}
	 * @param {Number}
	 * */
	constructor(x,y,r,d) {

		this.vars = {
			x:x,
			y:y,
			r:r,
			d:d,
			mass:12e+12,
			color:'#008000'
		}
	}
	/** 
	 * no need for a loop here but its called automatically
	 * @method loop
	 * */
	loop() {}
	/** 
	 * @method draw
	 * */
	draw() {
		ctx.beginPath();
		// handle the shadow on resize
		let s1 = 300;
		let s2 = 800;
		if (H < 550) {
			s1 = 100;
			s2 = 300;
		}
		var grd=ctx.createRadialGradient(75,50,s1,90,60,s2);
			grd.addColorStop(0,this.vars.color);
			grd.addColorStop(1,'#000');
		ctx.fillStyle=grd;       
		ctx.arc(this.vars.x,this.vars.y,this.vars.r,0,Math.PI*2,true);
		ctx.closePath();
		ctx.fill();
	}
	/** 
	 * @method get
	 * @returns {Object}
	 * */
	get() {
		return this.vars;
	}

}
/** 
 * @class Canon
 * */
class Cannon {
	/** 
	 * @param {Number}
	 * @param {Number}
	 * @param {Number}
	 * @param {Number}
	 * */
	constructor(x,y,r,d) {

		// @var {Number} An amount to resize objects in relation to the planet size
		this.divsize = 25;
		// @var {Number}
		this.r = r;
		// @var {Number}
		this.d = d;
		// @var {Object}
		this.pos = trig(x,y,r,d,false);
		// @var {Object}
		this.vars = {
			x:this.pos.x,
			y:this.pos.y,
			mountain:{
				coords:[],
				color:'#008000'
			},
			cannon:{
				barrel:{
					x:0,
					y:0,
					w:0,
					h:0
				},
				breach:{
					x:0,
					y:0,
					r:0
				},
				x:0,
				y:0,
				r:0,
				color:'#000000'
			}
		}

		this.mountain();
		this.makecannon();

	}
	/** 
	 * no need for a loop here but its called automatically
	 * @method loop
	 * */
	loop() {}
	/** 
	 * @method draw
	 * */
	draw() {
		ctx.fillStyle=this.vars.cannon.color;

		// cannon wheel
		ctx.beginPath();
		ctx.arc(this.vars.cannon.x,this.vars.cannon.y,this.vars.cannon.r,0,Math.PI*2,true);
		ctx.closePath();
		ctx.fill();

		// cannon breach
		ctx.beginPath();
		ctx.arc(this.vars.cannon.breach.x,this.vars.cannon.breach.y,this.vars.cannon.breach.r,0,Math.PI*2,true);
		ctx.closePath();
		ctx.fill();

		// cannon barrel
		ctx.beginPath();
		ctx.fillRect(
			this.vars.cannon.barrel.x,
			this.vars.cannon.barrel.y,
			this.vars.cannon.barrel.w,
			this.vars.cannon.barrel.h
		);

		// mountain
		ctx.fillStyle=this.vars.mountain.color;
		ctx.beginPath();
		ctx.moveTo(this.vars.mountain.coords[0][0],this.vars.mountain.coords[0][1]);
		for (let i = 0;i<this.vars.mountain.coords.length; i++) {
			ctx.lineTo(
				this.vars.mountain.coords[i][0],
				this.vars.mountain.coords[i][1]
			);
		}
		ctx.fill();

	}
	/** 
	 * establish the mountains polygon
	 * @method mountain
	 * */
	mountain() {
		// create the polygon coords for the mountain
		this.vars.mountain.coords[0] = trig(this.pos.x,this.pos.y+5,(this.r/(this.divsize/2)),(this.d-90),true);
		this.vars.mountain.coords[1] = trig(this.pos.x,this.pos.y,(this.r/(this.divsize/5)),this.d-10,true);
		this.vars.mountain.coords[2] = trig(this.pos.x,this.pos.y,(this.r/(this.divsize/5)),this.d+10,true);
		this.vars.mountain.coords[3] = trig(this.pos.x,this.pos.y+5,(this.r/(this.divsize/2)),(this.d+90),true);
	}
	/** 
	 * @method makecanon
	 * */
	makecannon() {
		// cannon wheel
		let xy = trig(this.pos.x,this.pos.y,(this.r/(this.divsize/6)),this.d,false);
		this.vars.cannon.x = xy.x;
		this.vars.cannon.y = xy.y+5;
		this.vars.cannon.r = Math.abs((this.r/this.divsize)-5);

		// breach
		xy = trig(this.vars.cannon.x,this.vars.cannon.y,this.vars.cannon.r,-120);
		this.vars.cannon.breach.x = xy.x;
		this.vars.cannon.breach.y = xy.y;
		this.vars.cannon.breach.r = this.vars.cannon.r/2;

		this.vars.cannon.barrel.x = xy.x;
		this.vars.cannon.barrel.y = xy.y-3;
		this.vars.cannon.barrel.w = this.vars.cannon.r*3;
		this.vars.cannon.barrel.h = this.vars.cannon.r;
	}
	/** 
	 * @method get
	 * @returns {Object}
	 * */
	get() {
		return this.vars;
	}
}
/** 
 * @method Canonball
 * */
class Cannonball {

	constructor(x,y) {
		//
		Vector.call(this, x, y);
		// @var {Number}
		this.startx = x;
		// @var {Number}
		this.starty = y;
		// @var {Number}
		this.deadtimer = 0; // this counts until the reset
		// @var {Number}
		this.resettimer = 1; // raise this to set a pause beween resets
		// @var {Object}
		this.vars = {
			x:x,
			y:y,
			r:3,
			dead:false,
			speed: new Vector(cspeed,0),
			dir:0,
			mass:2e+2,
			planet:{},
			color:'#fff'
		};
	}
	/** 
	 * 
	 * @method loop
	 * */
	loop() {

		//
		this.getplanet();

		// 
		if(this.collision())
			this.destroy();

		//
		if(this.vars.dead) {
			// move the cannonball off the stage
			this.vars.x = 1000000;
			// increment the timer
			this.deadtimer++;
			// if timer greater than reset
			if(this.deadtimer > this.resettimer)
				this.reset();
		}else{

			// THIS IS WHERE THE MAGIC HAPPENS
			// The math that is
			// @todo - reduce the code here
			//		   some of this can be combined

			// init empty vector
			let a = new Vector(0,0);

			this.vars.planet.rv.x -= this.vars.x;
			this.vars.planet.rv.y -= this.vars.y;

			// I was using V.length() however it isn't updated by the class
			// the maths to do so is easy enough so I just called thew standard method
			let d = this.vars.planet.distance;

			let n = this.vars.planet.rv.normalize();

			// stuck in the gravity well
			if ( d < 20 ) { 
			  n.x *= Math.pow(d/20,5);
			  n.y *= Math.pow(d/20,5);
			}

			let m = _planet.get().mass;
			 	m = m + this.vars.mass;

			a.x += n.x*(G*m)/(d*d);
			a.y += n.y*(G*m)/(d*d);

			this.vars.speed.x += a.x;
			this.vars.speed.y += a.y;

			this.vars.x+=this.vars.speed.x;
			this.vars.y+=this.vars.speed.y;
		}
	}
	/** 
	 * @method draw
	 * */
	draw() {
		ctx.fillStyle=this.vars.color;
		ctx.beginPath();
		ctx.arc(this.vars.x,this.vars.y,this.vars.r,0,Math.PI*2,true);
		ctx.closePath();
		ctx.fill();
	}
	/** 
	 * @method
	 * */
	addSpeed(d) {
		this.vars.speed.add(d);
	}
	/** 
	 * comment
	 * @method getplanet
	 * */
	getplanet() {
		this.vars.planet.rv = new Vector(_planet.get().x, _planet.get().y);
		this.vars.planet.r = _planet.get().r;

		// get the distance to the planet
		
		this.vars.planet.distance = distance(
			this.vars.x,
			this.vars.y,
			this.vars.planet.rv.x,
			this.vars.planet.rv.y
		);
	}
	/** 
	 * 
	 * @method collision
	 * */
	collision() {
		// check the distance to the planet
		if(this.vars.planet.distance < this.vars.planet.r)
		{
			return true;
		}else{
			return false;
		}
	}
	/** 
	 * 
	 * @method destroy
	 * */
	destroy() {
		this.vars.dead = true;
	}
	/** 
	 * 
	 * @method reset
	 * */
	reset() {
		this.deadtimer = 0;

		// place it back home
		this.vars.x = this.startx;
		this.vars.y = this.starty;
		// reset var
		this.vars.dead = false;
		// increment speed
		cspeed += c_speed;
		this.vars.speed = new Vector(cspeed,0);
	}
	/** 
	 * 
	 * @method get
	 * @returns {Object}
	 * */
	get() {
		return {
			x:this.vars.x,
			y:this.vars.y
		}
	}
}

/** 
 * @NOTE - I always thought that in the abstract Vectors and Tensors were essentially Objects
 * 		   This class proves my observation		   
 **/

/**
 * Vector
 */
function Vector(x, y) {
    this.x = x || 0;
    this.y = y || 0;
}

Vector.add = function(a, b) {
    return new Vector(a.x + b.x, a.y + b.y);
};

Vector.sub = function(a, b) {
    return new Vector(a.x - b.x, a.y - b.y);
};

Vector.scale = function(v, s) {
    return v.clone().scale(s);
};

Vector.random = function() {
    return new Vector(
        Math.random() * 2 - 1,
        Math.random() * 2 - 1
    );
};

/** 
 * comment
 * @method
 * */
Vector.prototype = {
    set: function(x, y) {
        if (typeof x === 'object') {
            y = x.y;
            x = x.x;
        }
        this.x = x || 0;
        this.y = y || 0;
        return this;
    },

    add: function(v) {
        this.x += v.x;
        this.y += v.y;
        return this;
    },

    sub: function(v) {
        this.x -= v.x;
        this.y -= v.y;
        return this;
    },

    scale: function(s) {
        this.x *= s;
        this.y *= s;
        return this;
    },

    length: function() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    },

    lengthSq: function() {
        return this.x * this.x + this.y * this.y;
    },

    normalize: function() {
        var m = Math.sqrt(this.x * this.x + this.y * this.y);
        if (m) {
            this.x /= m;
            this.y /= m;
        }
        return this;
    },

    angle: function() {
        return Math.atan2(this.y, this.x);
    },

    angleTo: function(v) {
        var dx = v.x - this.x,
            dy = v.y - this.y;
        return Math.atan2(dy, dx);
    },

    distanceTo: function(v) {
        var dx = v.x - this.x,
            dy = v.y - this.y;
        return Math.sqrt(dx * dx + dy * dy);
    },

    distanceToSq: function(v) {
        var dx = v.x - this.x,
            dy = v.y - this.y;
        return dx * dx + dy * dy;
    },

    lerp: function(v, t) {
        this.x += (v.x - this.x) * t;
        this.y += (v.y - this.y) * t;
        return this;
    },

    clone: function() {
        return new Vector(this.x, this.y);
    },

    toString: function() {
        return '(x:' + this.x + ', y:' + this.y + ')';
    }
};
            
          
!
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