In the beginning, there was Javascript
Over the last year, I’ve been working at a startup that’s building a framework which makes web apps for in-browser data visualization. A big part of this process is optimizing performance - when you have datasets that are hundreds of thousands of rows, you have to be careful with how it’s being handled, and make sure it’s snappy for users. A natural problem arises: Javascript is not built for this! It’s great for plenty of things, and browser companies have spent millions making it faster, but it’s still not the ideal language for this kind of work. When Python runs into this problem, they can turn to C, but where can in-browser Javascript go to get those speedups?
That’s the basic premise on which Mite was designed. By the end of the last paragraph you might’ve been thinking to yourself, “Well, isn’t that the point of WebAssembly? Just use [your favourite Wasm-supported language here]!”, and you wouldn’t be totally wrong. However, those languages come with their own ecosystems. Their own package managers, build systems, and CLIs. The Javascript ecosystem already has all that! If Svelte can have a compiler that’s tightly integrated with the ecosystem, why couldn’t a full-fledged language? Mite aims to be able to be used so smoothly alongside Javascript that it just feels like a natural extension of it.
Put simply, dipping into a high-performance language should be as simple as this:
import { Model } from "./llama.mite";
const model = new Model();
const response = model.generate("How many species of llamas are there?");
console.log(response);
The performance-intensive generation can be done in Mite, while the rest of the app is written in Typescript. And why not? With modern tooling like Vite, there is nothing stopping custom handling to be defined for .mite
files. In fact, this is already possible in some limited capacity with Mite today, with a simple addition of a plugin to your vite.config.js
. The barrier of entry is incredibly low, while the potential benefits are as much as you can push out of WebAssembly.
Features and Goals
Smooth integration with the Javascript ecosystem
One of the primary goals of Mite is to integrate with the Javascript ecosystem. Today, that means Rollup and Vite plugins allowing easy integration with existing projects, as well as fully supporting features like HMR. Typescript is also a first-class citizen, with .d.ts
files generated on the fly during development, enabling typesafe interactions between languages. In the future, that will mean things like full npm
support allowing Mite libraries to be distributed as both precompiled .wasm
binaries, and as source code that can be compiled on the fly, integrating with your own .mite
files. It will also mean structs defined in Mite will be usable as Javascript classes, allowing both method and property access as though they were regular old Javascript objects.
High performance, low binary size
Mite is designed with performance in mind. Low-level memory access allows for optimizations impossible when using Javascript, and WebAssembly has access to features that Javascript doesn’t, like SIMD. The compiler will be designed to take advantage of these features as much as possible. Additionally, since Mite is targeting a more specific usecase than general-purpose languages, more performant memory allocation features can be baked in, like defaulting to arena allocation1.
Since there’s tight integration with the Javascript ecosystem, Mite will also be able to take advantage of the existing tooling for optimizing its output - most notably, it will be able to use tree-shaking stats provided by Rollup to only compile what’s necessary.
A familiar syntax
Syntactically, Mite is very similar to most C-based languages. Heavy inspiration was taken from Rust, Typescript, and C, with the goal of making it feel familiar to a wide range of developers. This is important, as it makes it easier for developers to pick up and use Mite, and it makes it easier for the Mite compiler to be written in the first place.
Here’s an example of a simple Mite program:
struct coord {
x: f32,
y: f32
}
struct locations {
home: coord,
work: coord
}
fn commute(loc: locations): f32 {
let dx = loc.home.x - loc.work.x;
let dy = loc.home.y - loc.work.y;
return sqrt(dx * dx + dy * dy);
}
export fn main(): f32 {
let home = coord { x: 3, y: 4 };
let work = coord { x: 5, y: 6 };
let loc = locations { home, work };
return commute(loc);
}
When can I start using it?
Right now Mite is still in its very early stages. There’s still many design decisions and features that have to be thought of and figured out before Mite is ready for any sort of production use. However, there are some demos and examples available if you’re interested what it’s capable of today, if you’d interested in going through the hassle of linking local packages. Otherwise, stick around for more blog posts on the development of Mite, and when it’s ready for use, you’ll be the first to know!
Footnotes
-
This is pending a lot more thought, and probably a whole other blog post. ↩