Bemuse
A free and open source, web-based online rhythm action game.
Started in 2015, it is built based on modern HTML5 technology and libraries such as React and Pixi.js.
Play the game https://bemuse.ninja
# Etymology
The original name of the game was “beat☆music☆sequence”.
This name was derived from the file format that it uses, .bms
(Be-Music Source) (opens new window).
Later, I shortened the name to Bemuse, which happens to be a valid English word, meaning “to make confused”.
# Origin
I built this game as a playground in applying software engineering principles and practices. It is also my graduation project (opens new window). The project is open source, and has automated tests as well as a CI/CD pipeline around it.
Source code on GitHub https://github.com/bemusic/bemuse
# Features
The gameplay is heavily inspired and influenced by Lunatic Rave 2, beatmaniaIIDX, and O2Jam. It is key sounded, meaning that each individual note gets its own sound. The song will sound off if you did not hit the notes correctly.
I created this project because most PC rhythm games only work on Windows, and I use a Mac. I originally designed this project for desktops and iPads, but being web based, the game could be made to work on more devices as mobile devices become more powerful over time, bringing a truly cross platform experience.
In 2018, a 3D mode is added, and the game can be enjoyed in mobile phones.
Party mode lets multiple people play Bemuse together.
As of 2019, Bemuse has over 15,000 monthly active players.
# Development
# Software engineering
The first few weeks of development consists solely of setting up the project: webpack with loaders for Jade (opens new window), SCSS (opens new window), and 6to5 (opens new window) (now Babel (opens new window)). webpack-dev-server (opens new window), JSHint (opens new window), JSCS (opens new window), Jasmine (opens new window), Istanbul (opens new window), Code Climate (opens new window), Travis CI (opens new window), among others.
Developing software in an Agile way entails taking to heart that “Working software is the primary measure of progress.” Within a month, I deployed a teaser page (opens new window) with a technical demo, which proves that a rhythm game can indeed be built with current browser technology.
Software engineering for me is about striking a healthy balance between building the right thing and building it right. Although I want to ship things fast, I want to do it on top of a sound foundation.
Here are some technical choices that I felt very happy about when I look back:
Setting up linting tools from the start with pre-commit hooks and an automated code review bot (opens new window).
Using test-driven development for the game logic. As far as I remember, it never broke. Without the tests I wouldn’t dare replacing Ramda with Lodash (opens new window) in one go. You can even run the unit tests online (opens new window). A Japanese player once reviewed (opens new window) the game and said that “beta 版だが結構安定している。” (although the game is in beta, it is quite stable).
Setting up the app to allow multiple entry points. This lets me create pages to test components in isolation (opens new window) before Storybook existed, and also keep the teaser page (opens new window) and as well as our unit tests (opens new window) online.
Not using too many webpack plugins. This made upgrading to newer versions of webpack relatively painless.
Not using ES features lower than stage 2 or custom Babel plugins. This gave a lot of interoperability with tools that were not based on Babel, and also eased the transition to TypeScript compiler later on.
Using clean architecture and separating the core logic away from UI frameworks and libraries. This allowed me to replace Vue.js (v0.11) with Ractive (v0.7) (opens new window) and subsequently replace Ractive with React (v0.13) (opens new window).
# Experiment with confidence and explore with safety
Clean architecture and tests enable me to experiment with new things. It lets me make many two-way door decisions (opens new window). I can try out new (and perhaps not-so-mainstream) libraries and tools (such as webpack (at the time), Ramda, Ractive, and Bacon.js).
If they worked out well, then I could keep them (webpack being one example). Otherwise, if situation calls, I could switch to more boring (opens new window), stable, tried-and-true solutions (such as Lodash, React, and Redux) at a relatively low cost.
It allowed me to update the project to use new tools and follow the general direction of JavaScript community:
For UI library I initially used Vue (v0.11) (opens new window), then migrated (opens new window) to Ractive (v0.7) (opens new window), then migrated again (opens new window) to React (opens new window).
The project documentation used to reside directly on GitHub, then moved (opens new window) to Viewdocs (opens new window), then (opens new window) to Read the Docs (opens new window), and then (opens new window) with help from Resi Respati (opens new window), to Docusaurus (opens new window).
For testing framework I initially used Jasmine (opens new window), then moved (opens new window) to Mocha (opens new window) due to Jasmine’s lack of support for Promises at the time despite Promises becoming mainstream already.
For testing library I initially used expectations (opens new window) (because Jasmine), and then later on migrated (opens new window) to Chai (opens new window).
For test runner I started with plain Mocha running in the browser. I created my own test runner (opens new window) script that fires up a browser (opens new window), navigates to the test page, and then collects (opens new window) the test result. It then gets replaced with Karma (opens new window) once it has a perfect webpack support (opens new window). I did not migrate to Jest (opens new window) because our tests required access to browser’s APIs such as canvas.
For continuous integration I initially used Travis CI (opens new window). But since there are many CI services in the market, I commenced (opens new window) a CI war and also added CircleCI (opens new window), AppVeyor (opens new window) and Codeship (opens new window). In the end CircleCI wins (opens new window).
For Promise utilities, I initially tried out prfun (opens new window) and later replaced (opens new window) it with Bluebird (opens new window) which was more popular.
For code quality management I initially used Code Climate (opens new window), then stopped using it due to subpar support for JavaScript, replacing it with Coveralls (opens new window), and then (opens new window) with Codecov (opens new window), before coming back to Code Climate (opens new window) after it improved support for JavaScript and TypeScript. I also threw in SonarCloud (opens new window) after SonarQube gets a nice TypeScript support.
For source code transpilation I initially used 6to5 (opens new window) which is later renamed (opens new window) to Babel (opens new window). Getting tired of having to configure the plugins and presets and setting up regenerator, I replaced (opens new window) Babel with TypeScript (opens new window) compiler. One
tsconfig.json
file to rule ’em all!For code coverage instrumentation I initially used Istanbul (opens new window) but its lack of ES6 support at the time made me switch (opens new window) to Isparta (opens new window), which has some issues and that led (opens new window) me to create
babel-plugin-__coverage__
(opens new window) for use (opens new window) in Bemuse. That Babel plugin later got incorporated (opens new window) into a new major version of Istanbul (opens new window) to which I migrated (opens new window) the project to once again.For linting I initially used JSHint (opens new window) then later replaced (opens new window) with ESLint (opens new window). Extra linting is done with TypeScript Compiler’s
strict
mode, SonarCloud and Code Climate.For code formatting I initially used JSCS (opens new window) then later replaced (opens new window) with ESLint (opens new window), and subsequently (opens new window) with Prettier (opens new window).
Having a system for automated tests in place also allowed me to implement support for new file format in a single night (opens new window).
# Music
Bemuse plays song files in Be-Music Source (opens new window) .bms
file format.
It is the same format used in Lunatic Rave 2 and others, and has a thriving community around it where ‘BMS artists’ collectively publish hundreds of original songs every year (opens new window).
Most playable songs in the game, I reached out to BMS artists and asked for permission to use them in the game. Most artists are happy to allow me to use their songs for free, given that Bemuse is also a free game.
The background music for the game screens, as well as some playable songs, I composed myself.
- Tutorial song: BY☆MY☆SIDE
- Music selection song: BY☆MY☆SIDE (Ambient House Mix)
- Delay calibration song: AUTO±SYNCHRO