A talk I gave at Brooklyn JS #19 (May 21, 2015) about using Chromium Embedded Framework to connect Go and JS applications
The video of the demo is available here: https://www.youtube.com/watch?v=xR4dSiMEe-8
Here are the presenter notes which will give more context:
brooklyn js talk notes
5 facts about me
grew up less then a mile from here which leads me to
now repping the HV and live in a 350 year old house, save the date Oct 23rd: Catskills Conf is coming
3) Used to be the Chief Taco Officer at Paperless Post, now I'm the Chief Scientist and get to work on things
like I'm about to show you
4) Also been doing JS/Ruby/Go Perfomance and Organization consulting so hire me
5) I host a weekly podcast about Music, Food and Programming with MRB called Beats, Rye & Types (check out the episode with Jed)
A story of a yak shave
Started with the need to take a JS application that relied on canvas to turn data into images. It's important to note that we actually needed a full browser and not just JS - we needed canvas and a way to get that rendered buffer from canvas
Explored some node options
node-canvas - not precise or 1:1 poor font handling
node-webkit - almost there, but more geared to GUI applications
I really wanted to figure out a way to do this in Go because I've been writing a lot of Go and wanted to be able to reuse the communication, monitoring and tooling that I had already created.
Explored some existing webkit wrappers but nothing really worked well, until I found Chromium embedded framework (CEF) and cef2go.
cef2go takes advantage of the fact that cef exposes a simple C api/bindings and go has amazing support for embedding utilizing c code through cef2go
cef2go was in a sort of bad state, so I spent a bunch of time cleaning it up and getting it working on linux
eventually I got to a place where I could boot up a "browser" and execute JS in it. The cool thing is that because of Go's concurrency model it took no extra work to just loop and create many browsers. In this case, the idea would be 1-per CPU.
The next part was getting data back from JS into the go process and back to the client of the application.
The "normal" way to do this was to set up an http server and handle AJAX post calls from the JS. This actually was pretty easy, I combined the cef boot + http setup in a little wrapper called chrome farm. Created a little library to manage this called chromefarm.
It looked something like:
But i knew there had to be SOME way to call directly back to go from JS.
Turns out, through some roundabout ways, you can create JS methods bound to the V8 instance in CEF that are backed by NATIVE methods.
Native in this case means a C function that is compiled alongside CEF, but using the magic of CGO that method can actually be backed by a Go function.
Go => C => JS => C => GO
Creating these native methods was sort of a pain though, so using the property of JS that allows methods to have variable argument arity we can bind a single method as a "native" method that can then call any "registered" method on the go side.
In the JS this looks like:
cef.callback('myGoCallback', "any", "type", 0, "args", true)
And on the go side we just have to register what we want to do with this callback:
we also have to translate the individual types to the native types we want in Go.
But this actually works, and means we can pass data back and forth from a running browser to Go.
For us it enabled a really stable and fast platform for turning canvas based renders into images.
But ton's of possibilities, it could bring the power of anything you want to do in a modern chrome browser to go and really the other way around, too. You could have an app that's primarily JS but run in a cef shell and have access to all the powerful system level (and concurrent code) that go can provide.
I mean the real reason is because its an epic and beautiful hack.