osu! that can be played in a browser
Music might not be perfectly syncing (still being tested).
(under development)
In case you haven't heard of osu!, it's a game where players click circles on the screen, following rhythm of the music.
It runs on Firefox & Chrome. It doesn't run on Safari currently.
Note: This is an unofficial implementation of osu!. It has nothing to do with ppy. Scoring and judgement rules can differ from that of official osu!.
Modes other than osu! (std) are unsupported.
web page:
game in action:
Just set up a local web server with root directory located where index.html
is in.
- audio offset: no
- input offset: no
- custom skin: no
- beatmap hitsounds: no
- supported mods: EZ DC HR NC HD AT
Game field: 512 x 384 (in osu! pixels)
Circle diameter: 109 - 9*CS
(in osu! pixels)
Approach Time: AR<5? 1800-120*AR: 1950-150*AR
Scoring: (score for each hit) = judgement * (1 + (Combo before this hit) / 25)
Grading: (not implemented)
SS: acc = 1
S: acc > 0.95
A: acc > 0.9
B: acc > 0.8
C: acc > 0.7
300: +-(80 - 6*OD) ms
100: +-(140 - 8*OD) ms
50: +-(200 - 10*OD) ms
start of slider: same as hit circle
Spins required per second: OD<5? 3+0.4*OD: 2.5+0.5*OD
300: Progress >= 1
100: Progress > 0.9
50: Progress > 0.75
Everything runs at clientside. just to make it easier to deploy.
main.js
start load resources, setup environment, respond to webpage
settings.js
responsible for the settings panel on webpage: helping with visualization; store settings to localStorage; bring them into effect (before game starts)
osu.js
decode .osu file & load music
playback.js
defines `Playback` class, which creates & updates shapes in realtime, controls game update, judgement & display.
playerActions.js
check hits (only hitcircles and start of sliders), called by playback.js. also handles input events
score.js
calculates combo, score, accuracy, HP & set them in display
skin
stores all sprite textures in the game
hitsounds
stores hit sounds
Most materials in these folders are copyrighted to ppy, licensed under CC-BY-NC.
In addition, the free osu beatmap mirror site sayobot.cn
is used for displaying stars and length of beatmaps on the webpage.
plackback.js
is getting long and hard to read at first glance so I'll list them here.
var gfx // game field area
this.hits // array of hitobjects (including sprites & display info)
this.scoreOverlay // for calculating & displaying score, combo etc.
this.calcSize = function() // determin gaming field & element scaling
this.replaceHit = function(hit, zoom) // refresh position & scale of hitobjects
window.onresize = function() // window resize handling
this.newJudgement = function(x, y, depth, finalTime) // creating judgement object
this.updateJudgement = function(judgement, time) // set judgement display
this.createBackground = function()
this.createHitCircle = function(hit, objects = hit.objects)
this.createSlider = function(hit)
this.createSpinner = function(hit)
this.populateHit = function(hit)
this.updateBackground = function(time) // used for background diming animation
this.updateHitCircle = function(hit, time)
this.updateSlider = function(hit, time)
this.updateSpinner = function(hit, time)
this.updateHitObjects = function(time)
this.updateUpcoming = function(timestamp) // add upcoming hit objects to stage & destroy expired hit objects
this.playHitsound = function(hit, id)
this.hitSuccess = function(hit, points, time) // handles hit event of hit circle or start of slider
this.render = function(timestamp) // called in the main loop
// depth attributes of objects are for display layering
main.js
loads other scripts needed -> load skin & hitsounds & local beatmaps
import a beatmap: get blob -> zip import -> osu decode -> put on webpage
play: check for skin & hitsounds -> launch PIXI app & audiocontext -> set gaming area on page -> start game loop, enter playback & start loading music
-
If a slider is longer than given
PixelLength
, just truncated it -- This is what's done officially -
About mesh antialiasing: just blur the texture a little bit...
-
layering: approach circle > hit circle > slider ball > follow circle > slider body