2, 3 and 4 are spot on.
Theres's two connection pools one for reads one for write. Writes are batched every in a single transaction every 100ms and renders are pushed out to each connected user if there's a change max every 100ms (with back pressure). Reads are read only, and writes are transaction mode immediate.
Here's the schema:
CREATE TABLE IF NOT EXISTS cell(chunk_id INTEGER, cell_id INTEGER, state INTEGER, PRIMARY KEY (chunk_id, cell_id)) WITHOUT ROWID
And the options:
:cache_size 15625 :page_size 4096 :journal_mode "WAL" :synchronous "NORMAL" :temp_store "memory" :foreign_keys false
It's a pretty naive approach (1 billion rows).
Each user render is querying 2000 rows that then get converted to html elements, compressed and sent down on every change (including scroll). But, because renders are at most every 100ms changes effectively get batched.
On the whole this is relying on the brute force of SQLITE rather than anything too clever (eg: Hilbert curves or encoded chunks).
The point is that there could be any number of states. I wanted this to be quite general as for me this is more of a CRUD app demo.