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 is required to process package imports. If you need a different preprocessor remove all packages first.

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

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.

Editor Settings

Code Indentation

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

Visit your global Editor Settings.

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.

HTML

            
              <canvas width="860" height="480" id="canvas"></canvas>
<ul>
  <li>Right - Accelerate</li>
	<li>Left - Brake</li>
	<li>Up - Jump</li>
	<li>Down (while jumping or falling) - Swap Tracks</li>
	<li>1 - Disable forward movement</li>
	<li>2 - Enable forward movement</li>
</ul>
            
          
!

CSS

            
              body {
  background: #242424;
	color: #666;
	font-size: 8pt;
	font-family: sans-serif;
	padding: 0;
	margin: 0;
}
            
          
!

JS

            
              var _debug = {
  lockPlayerMovement: false,
  showPlayerInfo: false
};

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");

var Shape = {};

Shape.line = function() {
  this.x1 = 0;
	this.y1 = 0;
	this.x2 = 0;
	this.y2 = 0;
	this.lineWidth = 2;
	this.strokeStyle = "#000000";
	
	this.draw = function() {
		context.beginPath();
		context.moveTo(this.x1, this.y1);
		context.lineTo(this.x2, this.y2);
		context.lineWidth = this.lineWidth;
		context.strokeStyle = this.strokeStyle;
		context.stroke();
	};
};

Shape.circle = function() {
	this.x = 0;
	this.y = 0;
	this.radius = 0;
	this.lineWidth = 2;
	this.strokeStyle = "#000000";
	this.fillStyle = "transparent";
	
	this.draw = function() {
		context.beginPath();
		context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
		context.fillStyle = this.fillStyle;
		context.fill();
		context.lineWidth = this.lineWidth;
		context.strokeStyle = this.strokeStyle;
		context.stroke();
	};
};

Shape.text = function() {
	this.x = 0;
	this.y = 0;
	this.font = "10pt sans-serif";
	this.textAlign = "center";
	this.textBaseline = "middle";
	this.fillStyle = "black";
	this.fillText = "";
	
	this.draw = function() {
		context.font = this.font;
		context.textAlign = this.center;
		context.textBaseline = this.textBaseline;
		context.fillStyle = this.fillStyle;
		context.fillText(this.fillText, this.x, this.y);
	};
};

Shape.rectangle = function() {
	this.x = 0;
	this.y = 0;
	this.width = 0;
	this.height = 0;
	this.lineWidth = 2;
	this.strokeStyle = "#000000";
	this.fillStyle = "transparent";
	
	this.draw = function() {
		context.rect(this.x, this.y, this.width, this.height);
		context.fillStyle = this.fillStyle;
		context.fill();
	};
};

var Key = {
	_pressed: {},

	LEFT: 37,
	UP: 38,
	RIGHT: 39,
	DOWN: 40,
	SPACE: 32,
	SHIFT: 16,
	CTRL: 17,
	CAM_LEFT: 190,
	CAM_RIGHT: 188,
	
	DB_1: 49,
	DB_2: 50,
	DB_3: 51,
	DB_4: 52,
	DB_5: 53,
	DB_6: 54,
	DB_7: 55,
	DB_8: 56,
	DB_9: 57,

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

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

	onKeyup: function(event) {
		delete this._pressed[event.keyCode];
	}
};
window.addEventListener('keyup', function(event) { Key.onKeyup(event); }, false);
window.addEventListener('keydown', function(event) { Key.onKeydown(event); }, false);

var Camera = function(x, y) {
	this.x = x;
	this.y = y;
	this.radius = 4;
	this.velocity = {};
	this.velocity.x = 0;
	this.velocity.y = 0;
	
	this.addVelocity = function(x, y) {
		this.velocity.x += x;
		this.velocity.y += y;
	};
	
	this.update = function(lines, player) {
		this.x = player.x - (canvas.width / 2);
	};
};

var Player = function(x, y, r) {
	this.shapes = {};
	this.shapes.circle = new Shape.circle();
	this.shapes.radius = new Shape.line();
	
	this.x = x;
	this.y = y;
	this.radius = r;
	this.velocity = {};
	this.velocity.x = 0;
	this.velocity.y = 0;
	this.angle = 0;
	this.level = 1;
	
	var maxJumpDist = 12;
	var jumpDist = 0;
	
	var lasty = y;
	var lastx = 0;
	
	// States
	this.isJumping = false;
	this.isFalling = false;
	this.isSwappingLevels = false;
	this.isAlive = true;
	
	// Adds velocity to the player
	this.addVelocity = function(x, y) {
		this.velocity.x += x;
		this.velocity.y += y;
	};
	
	// Collides with the world
	this.collide = function(line, adjx, adjy) {
		var run = line.x2 - line.x1;
		var rise = line.y2 - line.y1;
		
		var slope = rise / run;
		var intersect = (line.y1) - (line.x1 * slope);
		var col = (slope * adjx) + intersect;
		
		var angle = radToDeg(Math.atan(rise / run));
		this.angle = angle;
		var normal = angle + 90;
		
		// Collide with track
		if(lasty < col && adjy > col) {
			this.y = col + (this.y - adjy);
			this.isFalling = false;
			this.isSwappingLevels = false;
			jumpDist = 0;
		} else {
			if(!this.isJumping) {
				this.isFalling = true;
			}
		}
	};
	
	this.update = function(lines) {	
		if(this.isAlive) {
			// Accelerate
			if(Key.isDown(Key.RIGHT))
				this.addVelocity(_debug.lockPlayerMovement ? 0.2 : 0.1, 0, 0);
			
			// Brake
			if(Key.isDown(Key.LEFT))
				this.addVelocity(_debug.lockPlayerMovement ? -0.2 : -0.1, 0);
				
			// Jump
			if(Key.isDown(Key.UP)) {
				if(!this.isFalling && !this.isJumping) {
					this.isJumping = true;
				}
			// Stop jumping
			} else {
				if(this.isJumping) {
					this.isJumping = false;
					jumpDist = 0;
				}
			}
		}
	
		this.y += 7.5; // gravity
		if(this.isAlive) this.velocity.x += _debug.lockPlayerMovement ? 0 : 0.2; // automatic accelerate
		this.x += this.velocity.x;
		this.y += this.velocity.y;
		
		// Decay velocity
		this.velocity.x *= 0.985;
		this.velocity.y *= 0.95;

		// Collision point
		var adjx = (this.radius * Math.cos((this.angle + 90) * Math.PI / 180)) + this.x;
        var adjy = (this.radius * Math.sin((this.angle + 90) * Math.PI / 180)) + (this.y + 2); // 2px padding for stroke width
		
		// Finds the line the player is going to collide with
		var playerPos;
		if(lines[0].level === 2) // background track
			playerPos = adjx / levelPrefs.width >> 0;
		else // foreground track
			playerPos = adjx / levelPrefs.width >> 0;
		
		// Loop through the lines and collide with the player
		for(var i = 0; i < lines.length; i++) {
			if(i === playerPos)
				this.collide(lines[i], adjx, adjy);
		}

		// Swap tracks
		if(!this.isSwappingLevels && !this.isJumping && this.isFalling && Key.isDown(Key.DOWN)) {
			if(this.level === 1) {
				this.level = 2;
				this.isSwappingLevels = true;
			} else {
				this.level = 1;
				this.isSwappingLevels = true;
			}
		}
		
		// Find the y direction the player is moving
		var yvel;
		var yfixed = this.y.toFixed(1);
		if(yfixed < lasty) yvel = 1;
		if(yfixed > lasty) yvel = -1;
		if(yfixed == lasty) yvel = 0;
		
		// Apply extra gravity to keep player on slope during downward movement
		// This prevents a bug where the player cannot jump while moving quickly
		// downwards due to being in a "falling" state.
		if(this.angle) {
			if(yvel < 0 && (this.angle < -33 || this.angle > 33) && !this.isJumping) 
				this.y += 2;
		}
		
		// Jumping logic
		if(this.isJumping) {
			var jumpVel = -15;
			jumpDist += 1;
			
			if(jumpDist > maxJumpDist) {
				this.isJumping = false;
				this.isFalling = true;
				jumpDist = 0;
			} else {
				this.velocity.y = jumpVel;
			}	
		}
		
		// Die when falling off the edge of the world
		if( adjy > canvas.height + (this.radius * 2) ||
			adjx < 0 ||
			adjx > lines.length * levelPrefs.width) {
			this.isAlive = false;
		}
		
		// Reset when the player dies
		if(!this.isAlive) {
			this.x = levelStart.x;
			this.y = levelStart.y;
			this.velocity = {x: 0, y: 0};
			this.isAlive = true;
		}
		
		lasty = this.y.toFixed(1); // last known y position
	};
	
	this.draw = function(camera) {
		this.shapes.circle.radius = this.radius;
		this.shapes.circle.strokeStyle = "#218ae0";
		this.shapes.circle.x = this.x - camera.x;
		this.shapes.circle.y = this.y;
		this.shapes.circle.draw();
		
		var x = (this.radius * Math.cos((this.angle + 90) * Math.PI / 180)) + this.x;
        var y = (this.radius * Math.sin((this.angle + 90) * Math.PI / 180)) + this.y;
		this.shapes.radius.x1 = this.x - camera.x;
		this.shapes.radius.y1 = this.y;
		this.shapes.radius.x2 = x - camera.x;
		this.shapes.radius.y2 = y;
		this.shapes.radius.strokeStyle = "#a7c6e0";
		this.shapes.radius.draw();
	};
};

var Line = function(x1, y1, x2, y2) {
	this.shapes = {};
	this.shapes.line = new Shape.line();

	this.x1 = x1;
	this.y1 = y1;
	this.x2 = x2;
	this.y2 = y2;
	this.strokeStyle = "#ffffff";
	this.level = 1;
	
	var lineClass = "line";
	
	this.setLevel = function(level) {
		this.level = level;
		lineClass = "line" + level;
	};
	
	this.draw = function(camera) {
		this.shapes.line.x1 = this.x1 - camera.x;
		this.shapes.line.y1 = this.y1;
		this.shapes.line.x2 = this.x2 - camera.x;
		this.shapes.line.y2 = this.y2;
		this.shapes.line.strokeStyle = this.strokeStyle;
		this.shapes.line.draw();
	};
};

function getNextPoint(frequency, offset, step, width, center) {
	return (Math.sin(frequency * step + offset) * width + center) >> 0;
}

function radToDeg(rad) {
	return rad * (180 / Math.PI);
}

var levelStart = {};
levelStart.x = canvas.width / 2;
levelStart.y = 100;

var camera = new Camera(levelStart.x, levelStart.y);
var background = new Shape.rectangle();
	background.x = 0;
	background.y = 0;
	background.width = canvas.width;
	background.height = canvas.height;
	background.lineWidth = 0;
	background.fillStyle = "#121212";
var player = new Player(levelStart.x, levelStart.y, 20);
	player.level = 2;
var lines = [];
var lines2 = [];

var levelPrefs = {
    width: 30,
    length: 600,
	frequency: 0.1,
	wavelength: 32,
	offset: 380,
	numLines: 50
};

var levelPrefs2 = {
    width: 30,
    length: 600,
	frequency: 0.2,
	wavelength: 48,
	offset: 260,
	numLines: 50
};

for(var i = 0; i < levelPrefs2.length; i ++) {
	var y = getNextPoint(levelPrefs2.frequency, levelPrefs2.frequency * Math.PI / 3, i, levelPrefs2.wavelength, levelPrefs2.offset);
	var y2 = getNextPoint(levelPrefs2.frequency, levelPrefs2.frequency * Math.PI / 3, i + 1, levelPrefs2.wavelength, levelPrefs2.offset);
	var line = new Line(i * levelPrefs2.width, y, i * levelPrefs2.width + levelPrefs2.width, y2);
	line.setLevel(2);
	line.strokeStyle = "#402712";
	lines2.push(line);
}

for(var i = 0; i < levelPrefs.length; i ++) {
	var y, y2;
	if(i % 100 > 90 && i % 100 < 100 ) {
		y = 800;
		y2 = 800;
	} else {
		y = getNextPoint(levelPrefs.frequency, levelPrefs.frequency * Math.PI / 3, i, levelPrefs.wavelength, levelPrefs.offset);
		y2 = getNextPoint(levelPrefs.frequency, levelPrefs.frequency * Math.PI / 3, i + 1, levelPrefs.wavelength, levelPrefs.offset);
	}
	var line = new Line(i * levelPrefs.width, y, i * levelPrefs.width + levelPrefs.width, y2);
	line.setLevel(1);
	line.strokeStyle = "#734720";
	lines.push(line);
}

var update = function() {
	context.save();

	// Use the identity matrix while clearing the canvas
	context.setTransform(1, 0, 0, 1, 0, 0);
	context.clearRect(0, 0, canvas.width, canvas.height);

	// Restore the transform
	context.restore();
	
	camera.update(lines, player);
	player.update(player.level === 2 ? lines2 : lines);
	
	if(Key.isDown(Key.DB_1)) _debug.lockPlayerMovement = true;
	if(Key.isDown(Key.DB_2)) _debug.lockPlayerMovement = false;
};

var draw = function() {
	background.draw();
	
	for(var i = 0; i < lines2.length; i++) {
		lines2[i].draw(camera, true);
	}
	
	player.draw(camera);
		
	for(var i = 0; i < lines.length; i++) {
		lines[i].draw(camera, false);
	}
};

var gameloop = function() {
	update();
	draw();
};

var animationFrame = window.requestAnimationFrame ||
	window.webkitRequestAnimationFrame ||
	window.mozRequestAnimationFrame    ||
	window.oRequestAnimationFrame      ||
	window.msRequestAnimationFrame     ||
	null;

if (animationFrame !== null) {
	var recursiveAnim = function() {
		gameloop();
		animationFrame(recursiveAnim);
	};

	// start the gameloop
	animationFrame(recursiveAnim);
} else {
	setInterval(gameloop, 1000.0 / 60.0);
}
            
          
!
999px

Console