Ear Sharpener is a simple game for practicing pitch perception. While ear training probably won't give you absolute or perfect pitch, it may improve your relative pitch. Try it for yourself!
https://ryanatkn.github.io/ear-sharpener
note: the piano sounds are out of tune, so don't play it too long
src/types.ts
.
The old ununsed hacky action implementation is in
src/utils/actions
.gameActions
to get the desired UX.
The four main sources of complexity include time-sequenced actions and side effects,
disabling input when appropriate, canceling async audio that should no longer be played due
to user input, and sequencing actions in the combo game differently than in standalone games.gameActions.guess
and gameActions.present
that sequence multiple dispatched actions and side effects based on changing store state.
Mocking the store with something like redux-mock-store is possible but impractical
because these action creators read the state changes to behave correctly,
so mocking the store would require mocking all state changes after each dispatch,
which duplicates reducer logic and adds significant complexity.
Given this dependency of actions on reducers,
I wrote the action tests to include the entire action/reducer flow
and assert the state changes rather than the dispatched actions,
and the reducers have simple smoke tests.PresentingAction
and PresentedAction
create the new states.
Doing this unfortunately fragments some of the logic.
For example, it would follow the rest of the app's conventions that present(game)
increments the presentCount
and plays the audio, but now they are separate code paths.
This design is more verbose and error prone than it feels like it should be.shouldComponentUpdate
,
made possible by Redux and immutable app state.npm install
npm install -g tsd
tsd install
npm test
npm start
# browse to http://localhost:8080
Uploaded by user beskhu at freesound.org. (public domain license) Transformed with Audacity into 3 second mp3s that fade out.
public domain (The Unlicense)