Skip to content

Instantly share code, notes, and snippets.

@towc
Created July 14, 2015 09:52
Show Gist options
  • Select an option

  • Save towc/82da53d79050c69d5a55 to your computer and use it in GitHub Desktop.

Select an option

Save towc/82da53d79050c69d5a55 to your computer and use it in GitHub Desktop.
Rainbow shiny comets ;)
<canvas id=c></canvas>

Rainbow shiny comets ;)

I'm also trying out a new way of denominating functions, tell me what you think!

As most of my latest pens, this was heavily inspired by the work of @jackrugile

A Pen by towc on CodePen.

License.

// even more awesome with this song playing ;)
// https://www.youtube.com/watch?v=0NKUpo_xKyQ
// ( lights )
var w = c.width = window.innerWidth,
h = c.height = window.innerHeight,
ctx = c.getContext( '2d' ),
opts = {
hitLine: h - 30,
hitIntensity: 1,
hitCoolDown: 4,
holeAttenuator: 30,
repaintAlpha: .02,
gravity: 0.01,
meteorCount: 15,
meteorSpawnProb: .05,
meteorBaseSize: 10,
meteorAddedSize: 10,
meteorBaseVelX: 2,
meteorAddedVelX: -4,
meteorBaseVelY: 2,
meteorAddedVelY: 3,
meteorTemplateColor: 'hsl( hue, 80%, light% )',
ashCount: 20,
ashSpawnProb: .08,
ashBaseSizeMultiplier: .1,
ashAddedSizeMultiplier: .1,
ashLifeMultiplier: 10
},
meteors = [],
holes = [],
explosions = 0,
tick = 0,
someAreDead = false,
tau = Math.PI * 2;
function init() {
ctx.fillStyle = '#151515';
ctx.fillRect( 0, 0, w, h );
ctx.globalCompositeOperation = 'destination-out';
ctx.save();
meteors.length = 0;
for( var i = 0; i < 100; ++i )
loop_update();
loop();
}
function loop() {
window.requestAnimationFrame( loop );
loop_update();
loop_render();
}
function loop_update() {
loop_update_tick();
loop_update_spawn();
meteors.map( meteor => meteor.update() );
holes.map( ( hole, i ) => hole.dead ? holes.splice( i, 1 ) : hole.update() );
}
function loop_render() {
loop_render_repaint();
loop_render_transform();
loop_render_hitLine();
ctx.globalCompositeOperation = 'lighter';
meteors.map( meteor => meteor.render() );
holes.map( hole => hole.render() );
}
function loop_update_tick() {
tick += .6;
tick %= 360;
if( explosions > 0 )
explosions -= 1 / opts.hitCoolDown;
someAreDead = false;
}
function loop_update_spawn() {
if( meteors.length < opts.meteorCount && Math.random() < opts.meteorSpawnProb )
meteors.push( new Meteor );
}
function loop_render_repaint() {
ctx.restore();
ctx.fillStyle = 'rgba(0,0,0,alp)'
.replace( 'alp', opts.repaintAlpha );
ctx.fillRect( 0, 0, w, opts.hitLine );
if( someAreDead ) {
ctx.fillStyle = 'rgba(20,20,20,.1)';
// ctx.fillRect( 0, 0, w, h );
}
ctx.save();
}
function loop_render_transform() {
ctx.translate( Math.random() * opts.hitIntensity * explosions,
Math.random() * opts.hitIntensity * explosions );
}
function loop_render_hitLine() {
ctx.fillStyle = 'rgba(20,20,20,alp)'
.replace( 'alp', opts.repaintAlpha * 2 );
ctx.fillRect( 0, opts.hitLine, w, h - opts.hitLine );
}
function Meteor() {
this.reset();
}
Meteor.prototype.reset = function() {
this.x = Math.random() * w;
this.y = 0;
this.vx = opts.meteorBaseVelX + Math.random() * opts.meteorAddedVelX;
this.vy = opts.meteorBaseVelY + Math.random() * opts.meteorAddedVelY;
this.size = ( opts.meteorBaseSize + Math.random() * opts.meteorAddedSize ) |0;
this.y -= this.size;
this.ashes = [];
this.color = opts.meteorTemplateColor.replace( 'hue', tick );
}
Meteor.prototype.update = function() {
this.x += this.vx;
this.y += this.vy += opts.gravity;
this.update_ashes();
this.update_checkDead();
}
Meteor.prototype.render = function() {
var color = opts.meteorTemplateColor.replace( 'hue', this.x / w * 100 + tick );
ctx.fillStyle = color.replace( 'light', Math.random() * 30 + 25 );
ctx.shadowColor = color.replace( 'light', Math.random() * 25 + 25 );
ctx.shadowBlur = this.size;
ctx.beginPath();
ctx.arc( this.x |0, this.y |0, this.size /2, 0, tau );
ctx.fill();
ctx.fillStyle = ctx.shadowColor;
this.render_ashes();
}
Meteor.prototype.update_ashes = function() {
if( this.ashes.length < opts.ashCount && Math.random() < opts.ashSpawnProb )
this.ashes.push( new Ash( this ) );
this.ashes.map( ash => ash.update() );
}
Meteor.prototype.update_checkDead = function() {
if( this.x > w + this.size )
this.reset();
if( this.y > opts.hitLine )
this.update_checkDead_explode();
}
Meteor.prototype.update_checkDead_explode = function() {
++explosions;
someAreDead = true;
holes.push( new Hole( this ) );
this.reset();
}
Meteor.prototype.render_ashes = function() {
this.ashes.map( ash => ash.render() );
}
function Ash( meteor ) {
this.reset( meteor );
}
Ash.prototype.reset = function( meteor ) {
this.meteor = meteor;
this.x = ( meteor.x + Math.random() * meteor.size * 2 - meteor.size ) |0;
this.y = ( meteor.y + Math.random() * meteor.size * 2 - meteor.size ) |0;
this.size = ( ( opts.ashBaseSizeMultiplier + Math.random() * opts.ashAddedSizeMultiplier ) * meteor.size ) |0;
this.life = this.originalLife = ( this.size * opts.ashLifeMultiplier ) |0;
}
Ash.prototype.update = function() {
--this.life;
if( this.life <= 0 )
this.reset( this.meteor );
}
Ash.prototype.render = function() {
ctx.fillRect( this.x, this.y, this.size, this.size );
}
function Hole( meteor ) {
this.x = meteor.x;
this.size = meteor.size * 10;
this.color = 'hsla(hue, 80%, 55%, .04)'.replace( 'hue', this.x / w * 100 + tick );
this.life = 0;
}
Hole.prototype.update = function() {
this.life += ( this.size - this.life ) / opts.holeAttenuator;
if( this.life > this.size * .60 )
this.dead = true;
}
Hole.prototype.render = function() {
ctx.shadowBlur = 0;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc( this.x, opts.hitLine, this.life, 0, tau );
ctx.fill();
}
init();
window.addEventListener( 'resize', function() {
w = c.width = window.innerWidth;
h = c.height = window.innerHeight;
ctx.fillStyle = '#151515';
ctx.fillRect( 0, 0, w, h );
ctx.globalCompositeOperation = 'destination-out';
ctx.save();
})
canvas {
position: absolute;
top: 0;
left: 0;
background-color: #222;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment