It sounds like FM synthesis[0] to me (so either an embedded FM software synthesizer or simply replaying samples of FM synthesis), so the timbre is quite unlike a "real" piano or PCM samples of a real piano. Timbre is a kind of "color" of "quality" of a sound - in my head it's similar to how different materials feel different to the touch. A violin and a trumpet can play the same "note" but their sounds have very different timbres. An apple and a ball might both appear red but have different surface textures.
The "piano" in the app sounds like the piano patches (presets) on FM-based synthesizers from the 1980s[1], where the designers tried to approximate the timbre of a piano using the limited (but fun!) possibilities of FM, while the transients and harmonics produced by a real acoustic piano are more complex.
The pitches and temperament sound OK to me, so I think what you are are hearing is the timbre. That is, the app is playing the "right" notes, but it's as if you're hearing a "different instrument", albeit one that is an early digital approximation of a piano sound.
[0] https://en.wikipedia.org/wiki/Frequency_modulation_synthesis
[1] e.g. the Yamaha DX7; here's one of its "piano" sounds for comparison: https://www.youtube.com/watch?v=F3rrjQtQe5A&t=214s
I have no problems explaining ie fb (f flat) or e# (e sharp) etc, in certain circumstances, but I'm a great believer in: don't mix the two (sharps and flats).
The other notes sound OK. The A and B might be a tiny bit flat too but my ear isn't good enough, and if they're flat they're nowhere near as far off as the F. OP, how are you specifying notes/pitches to the synthesizer?
The code of my implementation is at https://github.com/JNKKKK/pianochord.io/blob/master/src/libs...
I guess it't not a good choice to implement that part on my own, since there are many good libraries out there, like tone.js. I will probably deprecate my own synthesizer implementation and use tone.js.
All credit to @issa's ear. And yes, while you'd be unlikely to have encountered this bug if you were just telling a good existing synth to play an F, it's cool that you implemented the synth from scratch! Whether it's a good choice depends on the project goals.
[0] https://github.com/JNKKKK/pianochord.io/blob/master/src/libs...
Thanks again!