Every character speaks at its own pitch. Backspace is the only sound in the grammar that runs backwards.
The built package was not found. Run npm run build (or ./serve.sh, which builds too) and reload.
Then hold backspace and hear the difference. Enter commits; escape rejects and clears.
const pitch = (ch) => {
const c = ch.toLowerCase()
// a is the bottom rung, z the top
if (c >= 'a' && c <= 'z') return (c.charCodeAt(0) - 97) / 25
if (c >= '0' && c <= '9') return (c.charCodeAt(0) - 48) / 9
// space and punctuation are the gaps between
// words, so they sit low and stay out of the way
return 0.15
}
// A character arrives: the positional micro event,
// quieter and shorter than a grab. It is the most
// fired sound in any real interface.
Anti.play('tick', { value: pitch(ch) })
// A character leaves: fired at the pitch of the
// character being removed, not the one left behind.
Anti.play('undo', { value: pitch(removed) })
Ten of the eleven events attack, then decay. Undo runs the other way: it swells in slowly, bends down an octave (both endpoints on the ladder), and seats fast, so a deletion reads as an insertion in reverse.
No forward sound can say "taken back". Buttons, layers, forms, motion, and voice all map onto existing events; typing needed a new one.
The same rung, forwards and backwards: same length, similar loudness, the energy at opposite ends.
// tick: attack, then decay.
// undo: swell, bend down an octave, seat.
//
// One is the time mirror of the other, which is
// why a deletion does not need a new metaphor:
// it is the insertion, run the other way.
Anti.play('tick', { value: 0.6 })
Anti.play('undo', { value: 0.6 })