This week I learned about JSweet, a transpiler from Java to TypeScript/JavaScript.

So far I’ve kept my distance to JavaScript for various reasons, not only because I very much dislike typeless and weekly typed languages. I did some sidework with libGDX’ HTML back-end, which uses GWT - a very daunting beast, with slow compile times.

The design philosophy of JSweet raised my interest. I can type Java 8 code, in my favorite IDE, IntelliJ, with all the bells and whistles available. With JSweet’s strict mode, the transpiler makes sure I don’t use any Java runtime classes by accident. Its own Java core/runtime replacement seems adequate, and it supports a ton of well-typed JavaScript libraries through so-called candies, backed by its own Maven repository. There are Gradle and Maven plugins, too.

With a little work I got a small sample powered by three.js running. I just lost a good chunk of time figuring out how to properly embed the WebGL canvas. I believe I’ll never comprehend HTML/CSS.



This is the gist of the code. Pretty nice, familiar looking Java code. And most importantly, written with all the powerful tools available in IntelliJ. I can’t live without auto-completion, live code analysis and automatic import-management anymore.

HTMLElement container;
PerspectiveCamera camera;
Scene scene;
WebGLRenderer renderer;
Geometry geometry;
Material material;
Mesh mesh;
FPSMeter fpsMeter;

HelloJSweet() {
	container = document.getElementById("webglcanvas");

	double width = container.clientWidth;
	double height = width / 16.0 * 9.0;

	renderer = new WebGLRenderer();
	renderer.setClearColor(0xf0f0f0);
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(width, height);
	container.appendChild(renderer.domElement);

	camera = new PerspectiveCamera(75, width / height, 1, 1000);
	camera.position.z = 500;

	scene = new Scene();

	Light light = new DirectionalLight(0xffffff, 1);
	light.position.set(1, 1, 1).normalize();
	scene.add(light);

	geometry = new BoxGeometry(200, 200, 200);

	MeshLambertMaterialParameters materialParameters = new MeshLambertMaterialParameters() { {
		color = union(0x808080);
	} };

	material = new MeshLambertMaterial(materialParameters);

	mesh = new Mesh(geometry, material);
	scene.add(mesh);

	FPSMeterOptions fpsMeterOptions = new FPSMeterOptions() { {
		left = "auto";
		top = "auto";
		right = "5px";
		bottom = "5px";
		theme = "dark";
		heat = 1.0;
		graph = 1.0;
	} };

	fpsMeter = new FPSMeter(container, fpsMeterOptions);

	window.addEventListener("resize", this::onWindowResize);

	animate(0);
}

void animate(double time) {
	requestAnimationFrame(this::animate);

	fpsMeter.tickStart();

	mesh.rotation.x = Date.now() * 0.0005;
	mesh.rotation.y = Date.now() * 0.001;

	renderer.render(scene, camera);

	fpsMeter.tick();
}

Object onWindowResize(Event event) {
	double width = container.clientWidth;
	double height = width / 16.0 * 9.0;

	camera.aspect = width / height;
	camera.updateProjectionMatrix();

	renderer.setSize(width, height);

	return null;
}

You “loose” the ability to share Java desktop and HTML client code, like its possible with frameworks as libGDX+GWT, but you gain access to many well known JS libraries. Debugging isn’t an experience as smooth as I’m accustomed to - yet.

Compile (transpile) times seem very good. With some sample code as small as this, most time is taken by the Gradle process.

The infrastructure (build plugins, candies) is pretty impressive already. Discovery of libraries isn’t great, as there are simply too many of them already, and there are no desriptions or filters for candies, or the DefinitelyTyped repository.


tl&dr: JSweet is worth keeping an eye on.