itsAlive = itsAlive.default const fps = 60, // run at 60 frames per second gravity = -0.5, // y velocity change per frame jumpStrength = 15, // y velocity when you jump (pixels/frame) speed = 2, // x velocity when walking (pixels/frame) inputKeys = ["ArrowLeft", "ArrowRight", "ArrowUp"] // valid keypresses const [_tick, _arrowEvents, marioImage_] = [...Array(3)].map(itsAlive) const keypress = itsAlive({ ArrowLeft: false, ArrowRight: false, ArrowUp: false }) const mario = { x: itsAlive(0), y: itsAlive(0), vx: itsAlive(0), vy: itsAlive(0), dir: itsAlive('right') } setInterval( () => _tick.notify(), 1000/fps ) document.onkeydown = evt => (evt.preventDefault(), _arrowEvents.update(evt)) document.onkeyup = evt => _arrowEvents.update(evt) _arrowEvents.reducer( evt => { if ( inputKeys.indexOf(evt.key) >= 0 ) return {type: evt.type, key: evt.key} }) keypress .listenTo(_arrowEvents) .input(keypress, _arrowEvents) .reducer( (kp, evt) => { kp[evt.key] = {keyup: false, keydown: true}[evt.type] return kp }) mario.vx .listenTo(_tick) .input(keypress) .reducer( kp => { if (kp.ArrowLeft && !kp.ArrowRight) return -speed if (!kp.ArrowLeft && kp.ArrowRight) return speed return 0 }) mario.vy .listenTo(_tick) .input(keypress, mario.y, mario.vy) .reducer( (kp, y, vy) => { if ( y === 0 && kp.ArrowUp ) return jumpStrength if ( y > 0 ) return vy + gravity }) mario.x .listenTo(_tick) .input(mario.x, mario.vx) .reducer( (x, vx) => x + vx ) mario.y .listenTo(_tick) .input(mario.y, mario.vy) .reducer( (y, vy) => { return y + vy > 0 ? y + vy : 0 }) mario.dir .listenTo(_tick) .input(mario.vx) .reducer( vx => { if (vx > 0) return 'right' if (vx < 0) return 'left' }) marioImage_ .listenTo(_tick) .input(mario.x, mario.y, mario.vx, mario.dir, marioImage_) .reducer( (x, y, vx, dir, marioImage) => { const mario = document.getElementById('mario') let verb = 'stand' if (y > 0) { verb = 'jump' } else if (vx !== 0) { verb = 'walk' } src = `${verb}-${dir}.gif` if( marioImage !== src) mario.src = src mario.style.left = `${x}px` mario.style.bottom = `${53+y}px` return src })