Clayton Smith
Ian McGaunn
December 3, 2014


Description: TelePaint seeks to create an elegant solution for the lack of computer drawing tools that enable simultaneous collaboration. Real-time collaborative editors like Google Documents have existed for years in web browsers. Clojure has gotten a lot of attention for web development, but there haven't been very many art applications made with Clojure both on the server-side and the client-side. TelePaint uses Clojure at every level, and hopes to impress any artists who don't understand computers.


This is one of the earliest working versions of telepaint. This screenshot shows the canvases communicating with each other, making it so all users can draw on a shared canvas.

Concepts Demonstrated

  • Careful Handling of State Wherever possible, state modification is avoided. Of course, this is a network application and there will naturally be some state involved. When it is absolutely necessary to maintain state, it is done carefully.
  • First Class Functions Almost everywhere, first class functions are quite prolific.
  • Lazy Sequences Clojure's map function is lazy.
  • Hashtables Clojure maps (the data structure, not the higher order function) are implemented as hashtables
  • Lists Server represents information about each client connection as a list.

And more!

External Technology

Telepaint is a Clojure application and it makes use of a number of tools and libraries from the Clojure ecosystem. Telepaint uses WebSockets, a relatively new specification for full-duplex communications over a TCP channel.


  • leiningen -- for builds and dependency management
  • httpkit -- high performance http/websocket server
  • compojure -- a very nice routing library
  • cheshire -- server-side JSON parsing and serialization
  • clojurescript -- compile clojure into javascript for use in web applications
  • transit-cljs -- (transit implementation for clojurescript)

Favorite Lines of Code

Clayton Smith: I greatly enjoy this line of code. It paints on the canvas in a really cool way!
 (defn draw-line-on-canvas [canvas color x-pos y-pos]
     (let [context (.getContext canvas "2d")]
       (aset context "fillStyle" color)
       (. context beginPath)
       (. context moveTo x-pos y-pos)
       (.arc context x-pos y-pos 3 0 6.28 false)
       (. context fill)))

Ian McGaunn: binding a bunch of DOM events in an interesting way. It maps over a list of hashmaps which each have two keys, the first key called :event containing the name of the event to be handled and the second key called :fn containing a procedure that should be called when that event fires. The map has the result of assigning the field of @websocket* identified by the value associated with :event the procedure associated with :fn for every item in that list.

    (map #(aset @websocket* (:event %) (:fn %))
              [{:event "onopen"
                :fn socket-onopen}
               {:event "onclose"
                :fn socket-onclose}
               {:event "onmessage"
                :fn socket-onmessage}]))

Technology Used Block Diagram

Additional Remarks