Modern Rust-based library and command-line tool to turn Toki Pona text into the sitelen sitelen non-linear writing style. This is a complete rewrite of the original JavaScript implementation.
I created Sitelen Sitelen Renderer because I am fascinated by combining the artificial language Toki Pona with the non-linear writing style sitelen sitelen by Jonathan Gabel. While drawing sitelen sitelen by hand is a meditative activity, I was wondering if I could create them algorithmically. My first attempt was in the beginning of 2015 when I experimented with CSS Flexbox using SVG for the Glyphs. The algorithm was limited and doomed, so in October 2015 I started anew and set out to create SVG-only sitelen sitelen that can be exported and reused.
The project had since then been mostly dormant, with only a few minor fixes here and there. Now, in 2025 the project has accumulated so much rust, its codebase is now rewritten in Rust. This Rust rewrite maintains the same visual output and algorithm spirit while providing better performance, type safety, and modern tooling. This is still a hobby project that I work on in my spare time.
-
CLI-first: Native command-line tool for rendering Toki Pona text in the terminal
-
WASM support: Browser-compatible WebAssembly build for web integration
-
High performance: Significantly faster than the JavaScript version
-
Same visual output: Maintains compatibility with the original JavaScript implementation
-
Soon: Fixes such as adding a few nimi sin to complete the nimi ku suli.
- Lowercase text: All Toki Pona text is expected in lower case
- Sentence endings: All sentences should end with
.,!,?, or#(for banners). The colon:and,are also recognized as punctuation - Proper nouns in cartouches: Syllables appear in cartouches by writing words starting with an upper case letter:
jan Ponavsjan pona - Multiple sentences: Multiple sentences are automatically split by punctuation. Each sentence is rendered separately unless using
render_sentences() - Commas: Commas before
laandliare automatically removed, sotenpo, labecomestenpo la - Spacing: Double spaces are automatically normalized to single spaces
- Context separators: The
laparticle is recognized as a context separator and creates separate sentence parts
Install the CLI tool:
cargo install --path sitelen-cliOr build from source:
git clone https://github.com/olafjanssen/sitelen-sitelen-renderer.git
cd sitelen-sitelen-renderer
cargo build --releaseThe binary will be available at target/release/sitelen.
Build the WASM package for browser use:
cd sitelen-wasm
wasm-pack build --target web --out-dir pkgThis creates a pkg directory with the compiled WebAssembly module and JavaScript bindings.
The CLI tool can render Toki Pona text from command-line arguments, files, or stdin:
# Render text directly
sitelen "mi pona. ale li jo e tenpo." --output output.svg
# Render from file
sitelen --input text.txt --output output.png --format png
# Pipe text via stdin
echo "suno li pona." | sitelen --output output.svg
# With custom settings
sitelen --input text.txt --output output.svg --ratio 0.8 --stroke-width 2.0 --shadow
# Multiple sentences: by default each sentence goes to a separate file (output_1.svg, output_2.svg, …)
sitelen "mi pona. sina pona." --output output.svg
# Creates: output_1.svg, output_2.svg
# Combine all sentences into a single output file
sitelen "mi pona. sina pona." --output combined.svg --single
# Add box margin to prevent overflow (same on all sides, or per side)
sitelen --input text.txt --output output.svg --margin 10
sitelen --input text.txt --output output.svg --margin 10 --margin-top 20 --margin-bottom 20
sitelen --input text.txt --output output.svg --margin-left 5 --margin-right 5
# Output without embedded glyph definitions (references only)
sitelen --input text.txt --output output.svg --no-embed-glyphsFor web integration, use the WASM module:
import init, { render_svg } from './pkg/sitelen_wasm.js';
async function main() {
await init(); // Initialize the WASM module (required before use)
const svgString = render_svg("mi pona.", null);
document.body.innerHTML = svgString;
}
main();With a specific layout ratio:
await init();
const svgString = render_svg("ale li jo e tenpo. ale li pona.", 1.0);For automatic rendering of elements with the data-sitelen attribute (similar to the original JavaScript version):
import './auto.js';Then in your HTML:
<section data-sitelen>
ale li jo e tenpo. ale li pona.
</section>The auto-rendering script automatically:
- Initializes the WASM module
- Renders all elements with
data-sitelenattribute usingrender_sentences() - Injects default styles for proper display
- Handles multiple sentences with column layout
init(): Promise<void>- Initialize the WASM module (automatically called on import, but should be awaited before use)render_svg(text: string, optimal_ratio?: number | null): string- Render text to SVG stringrender_png(text: string, optimal_ratio?: number | null): Uint8Array- Render text to PNG bytesget_layout_ratios(text: string): string- Get all available layout ratios as JSON arrayrender_sentences(text: string, optimal_ratio?: number | null): string- Render each sentence separately, returns concatenated SVG stringsinit_glyphs(sprite_content: string): void- Initialize with custom glyph sprite (optional, overrides default embedded sprite)
The project includes several example pages demonstrating different use cases:
- Live input: Interactive input with live preview
- Editor: Markdown-style editor with live preview
- Layout engine: Optimally fills a page with Sitelen Sitelen by trying different layout ratios (experimental)
- Proverbs: Overview of Toki Pona proverbs from Sonja's book
- Tatoeba book: Generates a book-like layout of Tatoeba sentences
The renderer supports various configuration options:
optimal_ratio: Preferred aspect ratio (height/width, default: 0.8)stroke_width: Stroke width for glyphs (default: 2.0)shadow: Enable shadow effects (default: false)scale: Base scale for glyphs (default: 1.2)scale_skew: Scale skew for container overflow (default: 1.3)exportable: Embed glyph definitions in SVG (default: true)margin_top,margin_right,margin_bottom,margin_left: Box margin around the outer SVG to prevent overflow (default: 0). CLI:--margin <value>sets all sides;--margin-top,--margin-right,--margin-bottom,--margin-leftoverride per side.
The Rust version maintains the same visual output as the JavaScript version but with a different API:
This live sitelen sitelen web project is made possible by the great work done before me.
Toki Pona is an artificial language invented in 2001 by Sonja Lang as an attempt to understand the meaning of life in 120 words. In my own search, I am convinced this language should not be used to translate large bodies of text or as an actual means of communication but as a personal tool for soul searching.
Sitelen Sitelen or Sitelen Suwi is a project created by Jonathan Gabel in 2012 who created a non-linear writing style for Toki Pona inspired by Mayan script. I try to keep the algorithm behind the Sitelen Sitelen Renderer in the spirit of Jonathan's project and allow for the different ways of drawing the sitelen sitelen.
The vectorized glyphs are based on the excellent work by jan Same. To make my SVGs scalable I sadly had to get rid of the non-uniform stroke widths. Also I have slightly different ideas about how to use the containers so I took the liberty of slightly altering some glyphs.
Mind that this project is far from complete: many language constructs are not implemented yet. If you want to contribute in any way, you can for instance file issues you find or come up with clever new ways to put sitelen sitelen into action.
Cheers!
MIT (c) Olaf Janssen
Olaf T.A. Janssen olaf.janssen@pm.me