I wonder whether introducing slight per-pixel-per-draw noise into the values could be used here, to mask this sort of detail? Or would you be able to average it out somehow
In general noise is never the answer to fingerprinting. It makes gathering "accurate" data a little harder; it doesn't stop statistical analysis from revealing the truth.
This totally works as an approach! Brave does it for canvas data and a bunch of other web APIs as well. The "farbling" noise is deterministic per profile/origin combo to prevent being able to average it out across multiple page loads, but otherwise random.
If you own hundreds of domains, you're welcome to try, but there's no good reason to. Fingerprinting scripts rely on a canvas being identical between sessions to identify that the same user is even there in the first place. If you can already single out one user's activity across a hundred domains, you don't need to fingerprint them any further.
how do you ensure the noise doesn't change between calls? if I want to fingerprint how the letter "a" is rendered, how do you ensure that I can't try drawing "a1", "a2", "a3", etc. which are "different" draw calls, but still allow me to build a composite image of what "a" looks like?
I think you're right on both... adding noise would reduce the accuracy of the fingerprint, but boarder biases would continue to be detectable. For instance i'm aware of some quite significant biases between firefox and chrome in antialiasing fillRect() for subipixel values as the dimensions drop bellow 1x1 pixel that only a distracting amount of noise would be able to conceal for one sample. I know that's already detectable via the useragent string, but I suspect those who are focused on collecting those biases will find strong ones for other data points.