Skip to content

Overview

🎛️ Svelte Tweakpane UI wraps user-interface elements from the excellent Tweakpane library in a collection of 33 idiomatic, reactive, type-safe, carefully-crafted Svelte components.

The library makes it easy to quickly and declaratively add knobs and dials to your projects using components that feel like they belong in the DOM. It also augments Tweakpane with a few extra features for your convenience and enjoyment.

The components should be useful for integrating controls and value monitoring in parametrically driven artworks or simulations. For some use cases, it could also present a viable alternative to traditional component libraries altogether.

It’s open-source, MIT-licensed, extensively documented, and is compatible with Svelte 4 and Svelte 5. It should work anywhere Svelte works, including SvelteKit and “island” frameworks like Astro. Svelte Tweakpane UI is distributed as an NPM package, and the source code is available on GitHub.


Demo

🔗

Here we quickly build a UI to control a value through two different controls, and graph its change over time in another.

Wrapping the whole thing in a <Pane> component then turns an inline UI into a draggable and resizable control panel.

Svelte Tweakpane UI quick demo


Why use this?

🔗

You have values you want to control, and you’re using Svelte.

Tweakpane represents many years of development effort by Hiroki Kokubun. It has a well-designed, well-documented, and well-tested vanilla JS / TS API that’s generally a pleasure to use.

This works great in many contexts, and has the advantage of being quite dynamic and flexible. In a Svelte project, though, it always takes some extra work to make an imperative API play well with the frameworks’ paradigms for bindings, events, and component lifecycle management.

Svelte Tweakpane UI bridges this gap between Tweakpane’s imperative API and Svelte’s declarative and reactive approach.

To demonstrate, let’s integrate a simple example like range-bound number slider as an inline element in a Svelte component.

Vanilla Tweakpane example

🔗

This is what a transcription of the slider into a Svelte component via the Vanilla Tweakpane API might like in Svelte.

VanillaTweakpaneRange.svelte
<script lang="ts">
import { onMount } from 'svelte';
import { Pane } from 'tweakpane';
let params = {
speed: 50
};
let container: HTMLDivElement;
onMount(() => {
const pane = new Pane({
container
});
pane.addBinding(params, 'speed', {
min: 0,
max: 100
});
pane.on('change', () => {
// Trigger Svelte reactivity
params = params;
});
return () => {
pane.dispose();
};
});
</script>
<div bind:this={container}></div>
<pre>
{params.speed}
</pre>

It takes a bit of extra code and reasoning to create the PARAMS object, manage binding, listen for update events, tend to the component lifecycle, place the control in the DOM, etc.

Svelte Tweakpane UI Example

🔗

Here’s the same example transcribed into Svelte Tweakpane UI:

SvelteTweakpaneRange.svelte
<script lang="ts">
import { Slider } from 'svelte-tweakpane-ui';
let speed = 50;
</script>
<Slider bind:value={speed} min={0} max={100} />
<pre>
{speed}
</pre>

Lifecycle, DOM placement, binding, and event handling are all handled by the component. The component will also dynamically update when any of its props change, reconstructing the entire component if needed.

In this example, the code is a bit shorter, but the fundamental objectives are convenience, clarity of intent, and consistency with Svelte idioms — brevity is an occasional side-effect, but not a design priority.

Both yield the same range-bound numeric slider:

50

Why create this?

🔗

Three reasons:

  1. Just for fun. I’ve been using Svelte a fair bit lately for quick prototypes and tools, and have used Tweakpane for years. Combining the two spares some context switches when you’re rapidly iterating through a sketch.

  2. Expanded use-cases. I wanted to see how Tweakpane might work not just for quick control panels, but also as a (highly opinionated) “component library” that lives inline with the rest of your markup. This becomes a much easier experiment when components are the primary abstraction.

  3. Tweakpane evangelism. Advocacy for the human interface values Tweakpane demonstrates in its design:

    • High information density tamed by a rational grid and tidy typography
    • Tasteful hints of the skeuomorphic that imply controls’ behaviors before first interaction
    • Multiple means of value input and manipulation (type, drag, click, etc.)
    • A general confidence that your users can handle a slider or two without decompensating

Components

🔗

Svelte Tweakpane UI’s 33 components are exported from the svelte-tweakpane-ui package in a flat namespace. Internally, they’re structured into four basic functional groups: core, control, monitor, and extra, which are reflected in the documentation to help you find what you’re looking for.

Core components

🔗

The Core components are generally concerned with the structure and containment of other UI elements, and closely mirror the building blocks of the Vanilla Tweakpane API. You’ll see some familiar names here, like Blade and Binding — but bear in mind that higher-level components are available.

  • Binding
    Wraps the Tweakpane addBinding method.
  • Blade
    Wraps the Tweakpane addBlade method.
  • Folder
    Organize multiple controls into a collapsable folder.
  • Pane
    The root <Pane> component, used for organizing controls into a single group and controlling how and where the Tweakpane is displayed.
  • Separator
    A convenience component providing a subtle visual separator between controls, in the spirit of the HTML <hr> element.
  • TabGroup
    Contains a collection of <TabPage> components to be presented as a tabs.
  • TabPage
    Contains a collection of Tweakpane controls to be presented as a single group inside a <TabGroup> component.

Control components

🔗

These provide component abstractions for the full range of Tweakpane controls — plus some extras from the Plugins ecosystem. They are generally used to manipulate values.

Monitor components

🔗

Components optimized for visualizing values rather than manipulating them.

  • FpsGraph
    A control for monitoring and graphing frame rates over time.
  • Monitor
    Wraps the Tweakpane monitor binding functionality for boolean, number, and string values.
  • Profiler
    Measure and visualize multiple quantities over time.
  • WaveformMonitor
    Visualize multiple numeric values as a waveform.

Extra components

🔗

These are misfits and novelties.

  • AutoObject
    Rapid-development component which automatically creates a set of Tweakpane controls for an arbitrary object.
  • AutoValue
    Rapid-development component which automatically creates a Tweakpane control for an arbitrary value.
  • Element
    A component for embedding arbitrary HTML elements into a pane.

Design Approach

🔗

Batteries included

🔗

Svelte Tweakpane UI takes a monolithic approach. A single dependency and import delivers access to all components, plus controls from several Tweakpane Plugins, and additional Svelte-specific functionality. Tree shaking should keep your bundle size in check (but note the requisite import style).

Layered API

🔗

This library exposes both the core building blocks of Tweakpane’s vanilla JS API as Svelte components (e.g. Bindings, Folder, Pane, etc.), and builds on this foundation to provide a number of more specific components which provide both convenience, clarity of intent, and improved auto-completion through type narrowing (e.g. Slider, Point, etc.).

These more specific components trade some flexibility for ease of use. If that’s not to your taste, the lower-level components remain available for use. (<AutoObject> and <AutoValue> are also well-suited for situations demanding more dynamism.).

Let Svelte bind

🔗

Vanilla Tweakpane conveniently provides two-way bindings for manipulating object properties, but Svelte Tweakpane UI leaves binding up to the user via Svelte’s conventional approach. This makes bindings explicit, and allows most components to function either as monitors or inputs depending on whether their value property is bound.


Usage tips

🔗

Naming conventions

🔗

Type Names

Types are available for many component props, including the value prop. This makes it simple to be specific about types when working with Svelte Tweakpane UI components.

In general, type names take the form:

{ComponentName}{PropName}{NarrowedType}

For example, <RotationEuler> provides the following types:

  • RotationEulerValueTuple

    tuple type for the value prop

  • RotationEulerValueObject

    object type for the value prop

  • RotationEulerValue

    object / tuple union type for the value prop

  • RotationEulerOrder

    string literal union type for the order prop

  • RotationEulerUnit

    string literal union type for the unit prop

  • Etc. etc.

It’s verbose, but consistent and predictable.

(Development note: Generics are used extensively in the library’s internal implementation, but they are rarely exposed in the public API. Instead, to simplify discoverability, the more specific per-component named types for events and values are provided as explained above.)

Component Names

Most component names hew closely to the language used in the official Tweakpane documentation and API reference or in their originating Plugin, with one important exception:

The vanilla Tweakpane Tab is referred to as a <TabGroup> in Svelte Tweakpane UI, and the vanilla Tweakpane Page is referred to as a <TabPage>.

The names may seem quite terse, and likely to collide with other components (<Image>?), but this is by design since JavaScript and TypeScript provide reasonable techniques for creating namespaces and avoiding collisions.

For example, a namespace import onto the alias T:

<script lang="ts">
import * as T from 'svelte-tweakpane-ui';
</script>
<T.Button />

Importing components

🔗

During development, it is convenient and perfectly fine to import components from the root Svelte Tweakpane UI package directly, but in production it is recommended to use per-component imports to simplify the tree-shaker’s work and reduce bundle size.

For example, instead of:

<script lang="ts">
import { Button } from 'svelte-tweakpane-ui';
</script>
<Button />

You would use:

<script lang="ts">
import Button from 'svelte-tweakpane-ui/Button.svelte';
</script>
<Button />

P.S. Tree shaking resembles alchemy more than forestry… I’m open to suggestions for a better ways to encourage the tree shaker to do its work thoroughly.

”Island” framework compatibility

🔗

Svelte Tweakpane UI generally works fine with island frameworks like Astro with its Svelte integration, but there is one important caveat:

If you are explicitly defining a <Pane> and its children, you must do so in a stand-alone .svelte component, and then import that component into an Astro file… you can not use <Pane> directly in an .astro file.

Using multiple standalone components in an Astro components is fine — it’s nesting and order of instantiation in the island context that causes issues, and <Pane>’s children don’t correctly intuit that they’re not orphans.)

Dynamic Props

🔗

The vanilla tweakpane API is highly dynamic, and the availability of various options and behaviors can change depending on the type of a value or the presence of certain options.

Svelte’s component API is comparatively static, and the availability of props is typically fixed at compile time… but several Svelte Tweakpane UI components feature dynamic properties that are added / removed from the component depending on the presence or type of other props.

Dynamic props are used in cases where the clarity of a component’s purpose isn’t compromised by a bit of dynamism (e.g. <Pane>), or where the alternative would be to create a large number of very similar components (e.g. <Monitor>).

Svelte’s approach to supporting dynamic props is still evolving, and the right balance between flexibility and predictability is subject to reconsideration in subsequent versions of Svelte Tweakpane UI.

Autocompletion should adapt dynamically to show valid props available at any given time.

You may see warnings in the console at runtime if you change a dynamic prop on the fly while continuing to pass props valid only for a different permutation of dynamic props.


🔗

Tweak-oriented UI libraries

🔗

JS

C++

Svelte Tweakpane wrappers

🔗

Two other projects provide integration between Tweakpane and Svelte:

Svelte Tweakpane UI drew some inspiration from these projects, but was developed independently. The -ui suffix in svelte-tweakpane-ui is to avoid a naming collision with Karl Moore’s work.

Tweakpane adaptations for reactive frameworks

🔗

Tweakpane resources

🔗

Svelte 5 compatibility

🔗

As of version 1.4, Svelte Tweakpane UI is fully compatible with Svelte 5.

The library itself is (currently) written in the Svelte 4 style, which is now considered “Legacy” mode (vs. the new “Runes” mode) in Svelte 5, but the components will compile and work fine in Svelte 5 projects.

Svelte libraries are distributed as source, not as compiled components, so the version of Svelte actually used to build and run your Svelte Tweakpane UI components is determined by the Svelte version of the containing project — not by the library itself.

Due to some subtle differences in how Svelte 5 compiles Svelte 4 “legacy” code, there are some rare edge cases where you might see slightly different behavior in components compiled with Svelte 5 vs. Svelte 4. If you encounter an intractable case, please open an issue.

Longer-term, I’d like to rewrite the library in the Svelte 5 “Runes” style; there’s complexity in the current implementation around fine-grained reactivity that I’d love to simplify — but at this point I’d rather not maintain separate versions of Svelte Tweakpane UI for Svelte 4 and Svelte 5.