Skip to content

The whole interface, off the main thread

Interfaces that don't stutter

Components, layout, paint, text and input all run in a render worker. Your application logic runs in another. Whatever you compute, the frame still lands.

One canvasrender worker

Every pixel above is drawn by Gesso into a single canvas element.

Try it

Block the main thread for three seconds

Two copies of the same component. Only one of them notices.

What you get

Not a canvas. An engine.

A real layout engine

Flex and grid with CSS's own semantics, typed lengths, and text that wraps, clamps and sits on a baseline. Every case is checked against Chrome, so a box is the size you expect.

Layout in full
日本語の行の折り返しالنص من اليمينहिंदी की पंक्ति

Text in every script

Lines break where Chrome breaks them in eight scripts, with colour emoji, right to left paragraphs and web fonts loaded inside the worker. Both renderers, same result.

How text works

Twenty-seven components

Inputs, overlays, tables, trees and media. Every one themed, keyboard operable, and announced to assistive technology without you doing anything.

Browse the library

The model

Nothing re-runs

A state change writes one property on one node. There is nothing to memoise, no dependency array to keep honest, no view identity to reason about. Your component function runs once, so every closure in it is stable.

tsx
import { percent } from 'gesso-core';
import { Button } from 'gesso-components';
import { type ComponentContext, type Inputs, computed, input, internalState } from 'gesso-framework';

export function Counter(inputs: Inputs<{ label?: string }>, _context: ComponentContext) {
  const label = input(inputs.label, 'Count'); // inputs are cells; this one has a default
  const count = internalState(0);
  const caption = computed(() => `${label.value}: ${count.value}`);

  return (
    <row gap={12} x="center" y="center" width={percent(100)} height={percent(100)}>
      <text text={caption} textStyle="title" />
      <Button label="Add one" onClick={() => count.value++} />
    </row>
  );
}
What it draws

Neither version names a colour. primary is a theme token, resolved when it is painted, which is why the counter follows this page's light and dark toggle.

Reach for Gesso when

  • The screen is an application, not a document.
  • Heavy work has to run beside the interface without ever blocking it.
  • A hundred thousand rows have to scroll at sixty frames.
  • You want the interface tested without a browser at all.

Reach for something else when

  • The value of the page is being indexed and linked. A canvas has nothing for a crawler to read.
  • You need autofill, password managers and mobile keyboards behaving natively.
  • You need the DOM ecosystem more than you need the thread.
  • Browser extensions, view source or find in page are part of the product.
Is Gesso for your project?

A running project in about five minutes

The scaffold writes the worker, the shell and the first component, then gets out of the way.

$pnpm create:app my-app