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.
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.
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.
This is what a transcription of the slider into a Svelte component via the Vanilla Tweakpane API might like in Svelte.
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.
Here’s the same example transcribed into Svelte Tweakpane UI:
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
Three reasons:
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.
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.
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
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.
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.
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.
- Button
A humble but effective push button. - ButtonGrid
A grid of <Button>
components. - Checkbox
A checkbox. - Color
A color picker. - CubicBezier
A control for editing a bezier curve. Ideal for tweaking animation easing values. - File
A file input control. - Image
An image input control. - IntervalSlider
A twin-handled slider control for specifying range values. - List
An option list picker, similar to an HTML <select>
element. - Point
Wraps the Tweakpane point bindings. - RadioGrid
A grid of radio buttons. - Ring
A control evoking the focus ring on a proper camera lens. - RotationEuler
Integrates the euler rotation control from 0b5vr's tweakpane-plugin-rotation. - RotationQuaternion
Integrates the quaternion rotation control from 0b5vr's tweakpane-plugin-rotation. - Slider
A slider component providing fine-grained control over numeric values. - Stepper
A control for simple incremental value changes. - Text
A text field, in the spirit of the HTML <input type="text">
element. - Textarea
A multi-line text input field, in the spirit of the HTML <textarea>
element. - Wheel
A control evoking a dial on a proper camera body.
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
, andstring
values. - Profiler
Measure and visualize multiple quantities over time. - WaveformMonitor
Visualize multiple numeric values as a waveform.
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.
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).
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.).
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.
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
:
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:
You would use:
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.
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.)
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.
JS
C++
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.
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.