Skip to content

Browser Playground

The Nilakan playground is a focused, single-file environment for main.nlk. It runs entirely in your browser: source code, diagnostics, program output, drafts, and share payloads are not sent to a Nilakan backend.

Choose Run or press Ctrl+Enter on Windows and Linux, or Command+Enter on macOS. Output is streamed into the Output tab while the program runs. Stop terminates the execution worker and keeps any output already printed. Pressing the shortcut during a run stops that worker and starts the latest source revision.

Analysis uses a separate worker, so diagnostics continue to update while a program is running. If you edit during a run, that run may finish, but its result is labeled stale because it belongs to an older source revision.

The playground limits each run to 100,000 attempted commit rounds and 1 MiB of UTF-8 output. Source files and shared payloads are limited to 256 KiB.

Open Trace beside Run to show or hide the execution trace. On desktop, the pane sits to the right of the code editor; drag the vertical divider to adjust its width. On narrow screens, Trace uses the full workspace. Close it or choose its source-line button to return to the code. Closing the pane preserves the selected execution and scroll position.

The pane identifies the function and while statement that ran. Its line button reveals that loop in the editor. After an edit, the previous trace remains available with a stale notice; Run current code refreshes it.

Variables form columns and successive states form rows. Current state contains values at the start of each iteration. Within iteration contains local calculations. cond shows T to continue or F to stop; the Exit row marks termination. If the loop exits before a round, its calculation cells show —.

Use Loop to choose a source loop and Execution to distinguish repeated executions of that same loop. A parent row’s Nested loops links open the inner executions it ran. The Parent link returns to that enclosing iteration and selects its row. Nested state columns retain the prime marks used at that loop boundary. Function traces also show the argument values and source line for the call that produced them.

Guards using primed values also show Proposed state so the condition can be understood. An F row rejects that proposal and retains the current state. A notice identifies nested executions whose changes were rolled back by an enclosing loop.

Trace values use Pretty formatting by default. Numbers show at most seven decimal places, with very small non-zero values shown in scientific notation. Pretty formatting applies recursively to lists, tuples, sets, and dictionaries, so floating-point noise is also removed inside containers. Nested or long containers use multiple lines. Use Show raw values when you explicitly want the exact Nilakan representation; it replaces the formatted values rather than showing both versions.

Each variable heading has a configuration button with three modes:

  • Pretty uses the default formatting described above.
  • Raw always shows that column’s exact Nilakan representation.
  • Custom evaluates one JavaScript expression for each recorded value and displays its result.

A custom formatter is only an expression: there is no function declaration or return statement. For example:

value.toFixed(2);

The following values and helpers are available:

| Name | Meaning | | ----------------------- | -------------------------------------------------------------------------------------------------- | | value | The current value, converted to a convenient JavaScript type. | | kind | "number", "boolean", "string", "list", "tuple", "set", "dict", or "error". | | row | Row context with column, iteration, and phase ("current", "proposed", or "temporary"). | | pretty(value) | Applies the normal recursive Pretty formatter to a value. | | number(value, places) | Formats a number with at most places decimal places; the default is seven. |

Nilakan numbers, booleans, and strings become their JavaScript primitive equivalents. Lists and tuples become arrays, sets become Set objects, and dictionaries become Map objects. Errors become an object with name, message, and code. Use kind when an array might be either a Nilakan list or tuple. The expression must produce text, a number, or a boolean.

Display a decimal as a percentage:

`${number(value * 100, 1)}%`;

Join a flat list with arrows:

value.map((item) => number(item, 2)).join(" → ");

Put each row of a list of lists on its own line:

value.map((row) => row.map((item) => number(item, 2)).join(" ")).join("\n");

Put each dictionary entry on its own line:

[...value].map(([key, item]) => `${pretty(key)}: ${pretty(item)}`).join("\n");

The config panel previews the first three recorded values and offers Reset to Pretty. Custom formatting applies after execution stops. If an expression throws, returns an unsupported value, takes more than 250 milliseconds, or produces too much output, the entire column falls back to Pretty and displays a failure notice. The failed expression remains suspended until you edit and save it again.

Formatter settings are stored in this browser only. They belong to the local document and source loop, survive line insertion and guard edits, and are not included in share URLs. The Show raw values control temporarily overrides every column; Show formatted values restores the configured modes.

Custom formatters are trusted local JavaScript. Only code entered or already stored in your browser is evaluated. It runs in a disposable, time-limited worker so a loop cannot hang the playground, but it is not a security sandbox and can use APIs available to that worker. Do not paste formatter code you do not trust.

The table initially shows 100 rows plus the final recorded state. Use Show next 100 rows for more. Long containers show a structural summary and expand on demand. Extremely large values are explicitly marked as exceeding the display limit, and custom output is limited to 10,000 characters per cell and 100,000 characters per column.

Scroll within the table to read more rows or columns. Iteration and condition stay visible during horizontal scrolling. Expand a long value to read it in full. A failing iteration is recorded as an Error row with its last current state and its condition when the condition had already run. Truncated traces are marked as partial; Current shows the last known state and — means a value or condition was not recorded. An error notice links to Problems.

Monaco displays live Nilakan markers, hover details, error navigation, and supported quick fixes. The Problems tab lists current diagnostics without opening the Results panel automatically. Selecting a problem reveals its source range. Diagnostics from a stale failed run remain available in a separate collapsed section.

An ordinary playground URL opens the starter without saving it. When a local draft already exists, the playground shows when it was saved, a source preview, and an explicit choice to Resume draft or Start fresh. The first starter edit becomes the local draft and is then autosaved. Restored empty drafts are labeled as empty and offer direct starter and example actions instead of resembling a failed editor load. Once a draft is opened or edited, the address bar contains its exact source so copying the current URL reproduces the visible program without depending on local storage.

Word wrap, Results layout, and the draft editor view are stored locally as well. Shared snapshots and examples never overwrite that draft unless you explicitly choose Save as My Draft. Use Open My Draft to leave a shared snapshot or example without replacing the saved draft.

If another tab saves a newer draft, the playground shows a persistent choice to load it or keep editing. It never replaces the current source silently. If browser storage fails, the editor shows an inline warning and protects against accidentally closing the page until saving recovers.

Share updates the address bar and copies a compressed URL containing the exact source. No account or server storage is involved. Share URLs are immutable source snapshots. Examples use readable ?example=<id> URLs while they still match the curated source.

Editing an example or shared snapshot replaces the current history entry with an exact-source share URL and creates a recovery working copy in the current browser tab. The status bar distinguishes the canonical source from a saved working copy. Returning to the original route shows its canonical source and asks whether to restore the matching working copy or discard it and keep the original. Copying or reloading the exact-source URL restores the source directly from the address.

If the latest working copy cannot be saved, playground navigation returns to the current history entry and asks whether to stay, download the current source, or discard it and continue. Staying does not add or rewrite history entries, and choosing discard continues to the entry originally requested.

Browser and proxy URL limits vary. The playground rejects hashes longer than 32,000 characters and offers a .nlk download instead. Invalid, unknown, ambiguous, or corrupted routes are removed from the address bar before the starter or draft-resume choice is shown.

Open accepts .nlk and plain-text files that are valid UTF-8 and no larger than 256 KiB. Download writes the exact current UTF-8 source as main.nlk without formatting or adding a newline. Reset restores the current session’s origin: the starter for a draft, the selected example, or the source originally encoded by a shared link.

All destructive replacements require confirmation. Feedback stays next to the affected playground state; the editor does not use toast notifications.