If you want to build a modern web app, you typically need to use a few different tools: one set for the front-end, and another for the back-end. In addition, you would need to learn and use a different API (and often even a different programming language) for the back-end. However, the Web API is a rich and stable API, and the web browser is a stable, powerful and secure execution environment. So why then can we not use this execution environment for our back-end too? This is the question we asked ourselves at IBM Research, so we decided to try running a browser on the server. This turned out to work much better than we had anticipated!
Our first challenge was: how do we get many desktop browsers to talk to a browser on the server? The solution we came up with is simple: run a regular web server, and have it forward the requests to the server browser (which we will call the Execution Engine), which then processes the requests in tabs (i.e. loads the web page and runs a JavaScript function), and returns the results back. We created a controller tab that runs on the execution engine, which talks to the web server using a WebSocket, and then opens and closes (or re-uses) tabs on-demand. This simple setup was all that was needed to get the system functional.
Initially, we were concerned about how this might perform. After all, browsers are built to run on desktops, and hence to not utilize too many resources, so as to keep the rest of the system responsive. On a server however, we’d like maximum hardware utilization so that we can optimize the throughput and response times. So we built a proof-of-concept, and ran some performance tests. We ran the execution engine in headless mode, which makes it perform more like a back-end server. When we saw response times of around 20 milliseconds for a full round-trip function execution, our concerns were eased! Some more performance testing on a laptop showed that, in general, performance is about 10 times better* than a container-based serverless platform running the same function, on the same machine.
* This is a baseline comparative test, running a trivial JavaScript function on two platforms. Other tests with different workload or test profiles may reveal different performance results.
What we’d basically ended up with is a performant serverless platform, which we are calling Browser Functions, that can execute web front-end code on the back-end. As we explored this idea further, we realized some surprising benefits to this platform:
We can now do full stack development using just Web APIs. Need to read/write network resources? Use the API. Need to cache some data? Use . Need to blur an image? Use a CSS filter on an tag. Need to manage sessions? Use cookies. Need multi-threading? Use . Need native compiled speed (or a language other than JavaScript)? Use WebAssembly.
We already have all the tools needed to develop and debug the back-end code on our local development machines: the desktop web browser! We can develop locally, then upload the code to the server, and it just works.
The server is lightweight and easy to install and maintain. Running tens of thousands of simple requests on the server uses less than 2Gb of RAM.
We benefit from the proven, tested, and constantly updated security that the browser vendors have developed to protect one website from another. We use domain isolation to leverage this security by running each application in a separate domain.
We have hardware acceleration in the form of WebGL (if a 3D graphics card is available). We can leverage this by using JavaScript libraries that use WebGL, such as gpu.js or Tensorflow.js.
We have free, distributed “package management” in the form of