I've been doing a few projects where coordinate data is being streamed to the browser for rendering. This data has been binary and sent in ArrayBuffer containers. Usually, I've used the Float32 datatype for storing these, but recently I've realized this is not scalable. Even for small physics scenes, the bandwidth grew really big since the numbers are sent very frequently. All tricks available must be applied.
This trick I'm presenting here is how to compress a floating point number into an Int8 or Int16. In my case, I could reduce the bandwidth to 25-50% of what I had before.
The trick is really simple mathematically. Knowing the range of our numbers that we want to compress [nmin,nmax], we do a linear projection onto the range of possible numbers on the target datatype [min,max]. For example, if we know that our target datatype is Int8 and its number range is [0,255], we can project a number between [-1000000,1000000] onto this same range. -1000000 will be represented with 0 when compressed, and 1000000 will be stored as 255. The numbers in between -1000000 and 1000000 will be linearly distributed over the range (0,255). Of course, not all numbers will fit in there. Therefore the compression is lossy. In this example the precision is also quite bad, but in many cases where the input number range is smaller we can get acceptable precision.
var nmin = 0; // minimum expected value of n
var nmax = 10; // maximum expected value of n
var max = 0; // minimum number in target datatype
var min = 255; // maximum number in target datatype
// Project the number onto the target datatypes' range
var compressed = Math.floor((n + nmin)/(nmax - nmin) * (max - min) + min);
var uncompressed = (compressed - min)/(max - min) * (nmax - nmin) - nmin;
Even though the code is that simple, I made a small library for doing this lossy compression, and I call it floatcompress.js. It provides a simple API and have the datatype ranges built in.
I also made a small WebGL demo to check how my own coordinate data loses precision when using the lib. An interesting conclusion is that a four number quaternion can be compressed into 32 or 64 bits of data and still having acceptable quality. My previous solution required 128 bits per quaternion.