udraw: Multiplayer Drawing Canvas
udraw is a multiplayer drawing application like many other drawing apps out there which have surfaced since the rise of WebSockets. Mine happens to expand in size allowing very large drawings on an (unlimited?) sized surface. Each area of the canvas is broken into 256x256 pixel tiles which are drawn on a single HTML5 canvas seamlessly to make drawing on a large canvas possible. Here it is embedded below. Alternatively you can visit the full version at https://udraw.me
Panning around updates the URL to display your current location. This enables people to link to the same visible region. This is thanks to the browser history API.
The UI has the basics such as brush, pencil, colour picker along with the ability to move around with WASD key bindings and mobile touch gestures. When a draw stroke finishes all the changed tiles are set dirty and then the region is synced on the server side with a http PUT request. I hope to implement partial updates of a tile region by sending a PATCH request ‘diff’ rather than sending the entire tile to save bandwidth.
I was originally going to extend a JavaScript map library to add the drawing interface and saving on top. Most map libraries use the Spherical Mercator Projection but I’m glad I didn’t for mathematical nightmare reasons. It would have been cool to use a Mercator projection to allow the drawings to be mapped onto a sphere in WebGL. Maybe another time!
Another possible area that needs exploring is the ability to store diffs of the pixel data in some form of stack data structure on Redis. This would make undo rollback features possible. This might be quite easy in Redis with logging features it already has. For example keeping the last 20 update diffs of a given tile or have a TTL before flattening the image tile after a period of time.
A version hash could represent the latest state. ETags might be an ideal trick as these are sent when by the browser even on PUT update requests. If the tag has changed (by another person updating first) maybe try merge the changes together by getting the ‘newest’ changes applied on top of the current version in the client. It’s not likely this would work well, much like pushing code when your code branch is behind a few commits. This is where WebSockets come to the half-useful rescue by trying to simulate the remote drawing action when two people are drawing in the same area in the hope that both clients interleave the latest image state from their screen. It doesn’t work very well in many situations and browser vendors render lines and shadows quite differently on different OSs.
The Back End
The back end is accessed with a simple RESTful interface for loading and saving tile regions. Realtime events such as mouse movement or tool changes are performed over WebSocket for browsers to simulate the realtime aspects. After many road blocks trying to use Java I switched back to the NodeJS, Express, Socket.IO combo.
I can spin up many nodejs worker processes sitting behind nginx. A single Redis server stores all the tile data and relays the Socket.IO events to the other running NodeJS processes to keep everything in sync.
A note on HTTP/2
Annoyingly NodeJS still does not support HTTP/2 out-of-the box due to issues around SSL libraries and Express appearing to be dying based on GitHub activity. Multiplexing might reduce the latency for requesting multiple tile resources to the browser. Another cool feature HTTP/2 has is the ability to send Server Push messages which could send down cache invalidations when a tile has been updated by another user. I managed to clean the dust off the C compiler and started playing with nghttp2 which has a high level Boost ASIO library which been quite painless to write the REST interface on. I haven’t seen any significant speed improvements with HTTP/2 multiplexing. There are even some raising questions about its performance in high bandwidth usage applications and its potential to perform worse because of the extra overhead multiplexing streams over a single TCP pipe.
I will continue working on getting the bugs UI out in spare time and keep a watch on web app development with HTTP/2. I certainly don’t think it’s something you can just install and expect 40% speed improvements without any effort to tune what assets need pushed and how priorities and streams are handled.
Image Processing
Another advantage of switching to C/C++ is a multitude of available native libraries for image processing such as using ImageMagick or make it smart with OpenCV or get silly with procedural texture generation. One big request everyone wants is zoom! Pre-cached down-sampled images could be requested to see the entire canvas of drawings. I don’t think it would be easy to allow drawing on separate zoom levels as that might get quite expensive to keep all the levels up to date and not to mention the loss when drawing while zoomed out.
Another issue is removing dick pic drawings. That might be a fun and challenging area to explore with OpenCV. Being able to build a model derived from known dick pic drawings or use some form of cutting edge artificial neural networks to clean away rude drawings. I’ll call it APDRA (Adaptive Penis Detection and Removal Algorithm). Perhaps Google will already have plenty of research for this!