the logo: ‘sw&rpc’ written in black on top of a thick, bright yellow horizontal stripe

A library to do RPC1 between a main web page and a Service worker2. Developed while developing CIGALE: we were running neural network inference on the user’s device, but doing so on the same thread as the main UI3 code, making the application slow and laggy while inference was running (loading spinners not spinning smoothly, the UI reacting with delays, etc.)

The solution was to use Service workers, a technology that can run code off the UI thread.

But the API is a bit cumbersome: you have to send “message” events, and listen for them on the service worker code.

Thus, I made this library that, in a way that’s very similar to tRPC, allows you to define procedures (the types of their arguments, what they return, etc), implement them on the service worker code, and run them on the client as if it was a normal function.

Moreover, as this is intended for long-running functions, there is first-class support for reacting to progress updates as the function is running, and update the UI: for example, while downloading a large file, the download progress can be reported back to the callsite4, where it can be used to update a progress bar.

import { type } from "arktype"
import { Client, Server } from "swarpc"

// Define (shared file)
const procedures = {
  sum: {
    input: type(["number", "number"]),
    progress: type("0 <= number <= 100"),
    success: type("number"),
  },
}

// Implement (service worker)
server = Server(procedures)
server.sum(async ([a, b], setProgress) => {
  while (a < b) {
    a += 1
    setProgress((a / b) * 100)
  }
  return a
})

server.start()

// Call (web page)
await Client(procedures)
  .sum([2, 2], (p) => {
    console.log(`[${p}%] Computing…`)
  })
  .then(console.info)

made with

footnotes