Note
vibranceUI, developed by 59xa and Florian Butz, is licenced under the CC BY-SA 4.0 copyleft licence.
vibranceUI is a standalone C++20 SDK. It owns the renderer, UI,
windowing adapter, audio, media, neutral system-notification providers and
callback-driven cards, engine shaders, and all engine implementation code.
Applications consume its installed CMake package and never add this source tree
with add_subdirectory().
To build a new application, see Create an application.
The installed SDK includes starter scripts, a minimal template and the
vibrance_add_application CMake helper.
Configure dependency locations in a local .env.cmake using
envWindowsExample.cmake or envUnixExample.cmake as a starting point.
On Windows with MinGW:
mingwBuild.bat Release C:\dev\vibranceUI x64Windows builds accept x64, arm64, or all as the third argument:
mingwBuild.bat Release x64
mingwBuild.bat Release arm64
mingwBuild.bat Release allall builds and installs both Windows architectures. Defaults are symmetric:
| Architecture | Build directory | SDK directory |
|---|---|---|
| x64 | build/windows-x64/Release |
install/windows-x64 |
| ARM64 | build/windows-arm64/Release |
install/windows-arm64 |
Debug uses the corresponding configuration directory. With an explicit base
prefix, all installs to <prefix>/windows-x64 and <prefix>/windows-arm64.
A single-architecture command honours its explicit prefix exactly. A direct
Windows CMake configure also defaults to install/windows-<architecture>.
Windows builds use the configured MinGW toolchain and GNU Make. ARM64 requires
UCRT LLVM-MinGW; set LLVM_MINGW_PATH in the environment or .env.cmake.
Install MSVC ARM64 build tools for the Composition and WinRT companion DLLs.
GLFW and FreeType must be source trees or ARM64 installs. FFmpeg requires a
matching ARM64 SDK selected through FFMPEG_ARM64_PATH.
On Unix-like systems:
./unixBuild.sh Release /opt/vibranceUIThe selected prefix is a relocatable SDK:
<prefix>/
bin/vibrance_engine shared library
include/vibranceUI/... public API
lib/... import/static metadata
lib/cmake/vibrance_engine/vibrance_engineConfig.cmake
share/vibrance_engine/vibrance_engine_manifest.json
share/vibrance_engine/toolchains/windows-arm64-llvm-mingw.cmake
A downstream CMake project needs only:
find_package(vibrance_engine 0.1 CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE vibrance::engine)
vibrance_engine_copy_runtime_companions(my_app)Pass the SDK prefix through CMAKE_PREFIX_PATH, or point the app's
VIBRANCE_ENGINE_ROOT cache variable at it.
Reusable object models and retained controls are part of this same target; no per-control library or implementation source needs to be added downstream:
#include <vibranceUI/time/countdown_timer.h>
#include <vibranceUI/audio/loopback_capture.h>
#include <vibranceUI/audio/spectrum.h>
#include <vibranceUI/display/display.h>
#include <vibranceUI/media/media.h>
#include <vibranceUI/notifications/notifications.h>
#include <vibranceUI/ui/components.h>
#include <array>
CountdownTimer timer { 5 * 60 };
timer.start(0.0); // Supply the application's monotonic time.
AudioSpectrumProcessor spectrum;
std::array<float, AudioSpectrumProcessor::kFftLength> monoSamples {};
spectrum.push_samples(monoSamples); // Samples can come from any source.
spectrum.update(1.0 / 60.0);
// Or let the engine's platform source feed the processor directly.
AudioLoopbackCapture capture(spectrum);
capture.start();
// UiContextMenuItem, UiAnimatedCharacterTextView, and the dialog builders are
// ready to use with any UiBuilder owned by an engine window or panel.<vibranceUI/media/media.h> similarly exposes UTF-8/time/aspect helpers,
artwork presentation and playback animation, RevisionedMediaSlot, and
MediaArtworkView. It also includes the portable GlobalMediaSession provider,
status, commands, and snapshot contract from <vibranceUI/media/session.h>.
The optional installed Win32 companion supplies GSMTC; other platforms fail
closed until a provider is implemented. Applications provide refresh cadence,
optimistic UI state, and product routing, while the SDK owns provider and
renderer-local behavior.
<vibranceUI/notifications/notifications.h> exposes
SystemNotificationProvider, access state, revisioned snapshots, neutral
items, explicit dismissal/source-activation commands, and application-icon
resolution. The provider never persists, claims, groups, or displays records;
applications retain those product decisions. On Windows it privately loads the
installed shared interop companion. Other platforms are safe to compile and
construct and return an explicit unsupported diagnostic.
<vibranceUI/notifications/card.h> builds a complete retained notification card
from presentation-only strings, media handles, and callbacks. It also owns text
filtering, message wrapping, height measurement, and compact group-fan setup.
The options object has useful defaults, so an app does not need to reproduce the
card's entity graph:
UiNotificationCardOptions card {};
card.parent = notificationRoot;
card.content = {
.header = "Calendar",
.title = "Stand-up",
.message = "The meeting starts in ten minutes.",
.elapsedText = "now"
};
card.callbacks.activate = [] { open_calendar(); };
card.callbacks.dismiss = [] { dismiss_from_model(); };
UiNotificationCardHandle view = ui_create_notification_card(
ui, fontAtlas, localisation, card);<vibranceUI/notifications/stack_layout.h> independently places measured
cards down a right-edge stack, including compact backing-layer depth. Supply
only sizes and group counts; no application notification model is required.
SystemNotificationProvider notifications;
if (notifications.bridge_loaded())
{
// Call request_access() from a user-initiated permission flow when needed.
notifications.refresh(); // Read-only: this never claims native records.
for (const SystemNotificationItem& item : notifications.snapshot().items)
{
consume_notification(item);
// Persist application state before an optional dismiss(item.providerId).
}
}<vibranceUI/display/display.h> provides stable monitor targeting, independent
feature trackers, edge geometry, and optional JSON preference storage. Product
code supplies its preference keys and decides which interface owns each
tracker.
Use <vibranceUI/engine.h> when a small application prefers one SDK umbrella
include. Narrow headers remain available for larger translation units.
The installed package exposes vibrance_engine_MANIFEST_FILE for build tools.
At runtime, include <vibranceUI/core/engine_manifest.h> and call
vibrance_engine_manifest() to query the same engine name, semantic version,
full display build, and ISO last-updated date directly from the loaded DLL.
The full display build uses
<version>-<UTC date>.<sequence>-<stage> (for example,
0.6.0-20260903.0-a). Configure VIBRANCE_ENGINE_RELEASE_STAGE as alpha,
beta, release-candidate, or release; the manifest abbreviates these as
a, b, rc, and r. Increment VIBRANCE_ENGINE_BUILD_SEQUENCE when
creating another engine build on the same UTC day.
UiWindow is the default entry point for a normal application window. It owns
GLFW, the surface-bound Engine, custom chrome, input callbacks, resizing, and
the frame loop:
#include <vibranceUI/engine.h>
#include <utility>
int main()
{
UiWindowOptions options {};
options.title = "Hello Vibrance";
options.size = { 720, 480 };
options.fontPath = "assets/Inter-Regular.ttf";
options.build = [](UiWindowContext& view) {
const entt::entity card = view.ui.block("#20242EFF", 20.0f);
view.ui.place(card).inside(view.root).fill(24.0f).layer(0);
const entt::entity title = view.ui.text(
"Hello, world",
view.fontAtlas,
28.0f);
view.ui.place(title)
.inside(card)
.at(UiAlignment::eTopLeft)
.offset(24.0f, 24.0f)
.layer(1);
};
UiWindow window(std::move(options));
return window.run();
}Placement values are logical pixels and remain DPI-aware. Use
GlfwWindowHost when an application needs several independently controlled
windows or monitor-edge placement. The existing GlfwPanelWindowHost,
Renderer2DScene, and raw GLFW adapters remain available for specialized
chrome, retained scene mutation, and custom process loops.
UiScrollViewportOptions is the application-facing scroll control. One call
creates its positioned surface, rounded mask, wheel input, moving content,
scrollbar, and a border that fades in while shrinking to the viewport. The
surface, content, and scrollbar remain stationary throughout that entrance.
The content callback runs immediately and exposes the correct parent and render
layer:
UiScrollViewportOptions countries {};
countries.contentHeight = 24.0f + names.size() * 32.0f;
countries.buildContent = [&](const UiScrollViewportBuildContext& content) {
for (std::size_t index = 0; index < names.size(); ++index)
{
const entt::entity label = content.ui.text(
names[index], view.fontAtlas, 14.0f);
content.ui.place(label)
.inside(content.content)
.at(UiAlignment::eTopLeft)
.offset(12.0f, 12.0f + index * 32.0f)
.layer(content.layer, content.order + index);
}
};
UiScrollViewportHandle viewport = ui_create_scroll_viewport(
view.ui,
view.root,
UiAlignment::eCenter,
{ 0.0f, 40.0f },
{ 456.0f, 248.0f },
std::move(countries));All dimensions are logical pixels. Set entrance.enabled = false for an
already-visible viewport; colors, outline, corner radius, scrollbar behavior,
edge fades, and scroll callbacks are regular options. The returned handle also
exposes each retained entity for advanced styling without requiring it.
Window hosts also expose independent initialization policy for focus, taskbar
presence, and Alt-Tab/window-cycle presence. Disabling one of these policies
causes the native window to be configured before its first visible frame.
GlfwWindowPositionOptions adds reusable monitor-work-area alignment (including
centering, stable monitor ids, reference-point targeting, margins, and offsets)
across GlfwWindowHost, GlfwPanelWindowHost, and UiWindow.
GlfwPanelWindowHostOptions::topDragHeight can independently opt a borderless
panel into a logical-pixel top drag strip after its template factory runs;
title and content geometry remain unchanged, and zero disables the strip.
Use GlfwPanelWindowHost::refresh_template() for later rebuilds so this and
other host-owned policies are reapplied.
<vibranceUI/ui/page_stack.h> owns the common keep-alive page pattern: each
page is an entity subtree, exactly one registered root is visible, and page IDs
connect directly to a history-enabled icon button or navigation cluster. Apps
do not need one visibility boolean per page.
enum class Page : std::size_t
{
eWelcome,
eOptions,
eSummary
};
const entt::entity welcome = ui_create_page_root(view.ui, view.root);
const entt::entity options = ui_create_page_root(view.ui, view.root);
const entt::entity summary = ui_create_page_root(view.ui, view.root);
const UiPageStackHandle pages = ui_create_page_stack(
view.ui.scene(),
{
{ ui_page_id(Page::eWelcome), welcome },
{ ui_page_id(Page::eOptions), options },
{ ui_page_id(Page::eSummary), summary }
},
{ .initialPage = ui_page_id(Page::eWelcome) });
UiIconButtonOptions backOptions {};
backOptions.useHistory = true;
backOptions.historyDirection = UiNavigationDirection::eBack;
backOptions.initialPage = ui_page_id(Page::eWelcome);
backOptions.onNavigatePage =
ui_page_stack_navigation_callback(view.ui.scene(), pages);Build controls beneath the corresponding root, whether their builders are in
the same translation unit or another .cpp. Forward links call
ui_navigation_push_history; direct, non-history selection can call
ui_page_stack_show. ui_page_stack_add_page supports a root registered after
the stack is created. Duplicate IDs/roots and unknown destinations are rejected
without hiding the active page.
<vibranceUI/ui/text_block.h> adds document-style text without requiring apps
to insert manual newlines. Block placement and line alignment are independent,
so a centered fixed-width document can still use a shared left edge:
UiTextBlockOptions body {};
body.width = 520.0f;
body.fontSize = 14.0f;
body.wrapMode = TextWrapMode2D::eWord;
body.textAlignment = TextHorizontalAlignment2D::eStart;
body.lineHeightMultiplier = 0.96f;
body.lineSpacing = -0.5f;
body.paragraphSpacing = 6.0f;
body.characterSpacing = 0.0f;
body.wordSpacing = 0.0f;
body.placement = UiAlignment::eCenter;
body.style = make_flat_text_style("#55585EFF");
UiTextBlockHandle paragraph = ui_create_text_block(
view.ui,
view.fontAtlas,
pageRoot,
"A long paragraph wraps automatically inside the authored width.",
body);Use eStart, eCenter, eEnd, or eJustify for each line and eWord,
eCharacter, or eNone for wrapping. Start/End follow right-to-left text
direction. Explicit newlines receive paragraphSpacing; automatically wrapped
lines receive the chosen line height and line spacing. set_text_entity
reflows an existing block, including after localization changes, and
ui_set_text_block_layout updates its editorial settings in logical pixels.
Use a shared-width layout_group or UiStackLayout when headings and body
blocks need the same edge and controlled spacing between differently styled
sections.
UiLiquidMergeOptions in <vibranceUI/ui/surfaces.h> exposes the analytic
liquid connection used by detachable interfaces. It works between any two
circular surfaces or equal-height pill end caps and is not tied to an island or
timer. Endpoint centres and diameter use framebuffer coordinates so the two
surfaces do not need a common layout parent.
UiLiquidMergeOptions merge {};
merge.enabled = liquidMergeEnabled;
merge.firstCenter = mainSurfaceCenter;
merge.secondCenter = detachedSurfaceCenter;
merge.diameter = surfaceDiameter;
merge.amount = mergeProgress; // 0.0 detached, 1.0 merged
merge.opacity = mergeOpacity;
merge.layer = -1;
entt::entity bridge = ui_create_liquid_merge(ui, merge);
// Update the same entity while either endpoint moves.
ui_update_liquid_merge(ui.scene(), bridge, merge);Set enabled to false, or call ui_set_liquid_merge_enabled, when an
interface should render without the effect. Paint, ordering, always-on-top
behaviour, and dynamic-cache participation are also regular options.
Renderer-specific implementation is separated by API:
engine/src/
renderer/vulkan/ Vulkan renderer
animation/ Animation importers
graphics/ Shared material routing
platform/win32/ Windows Composition, interop and native tray
platform/macos/ macOS native adapters
platform/portable/ Explicit unsupported-platform fallbacks
RenderBackend identifies the API that draws the UI. PresentationBackend
is separate because Windows currently keeps Vulkan as the renderer and uses a
D3D11 swap-chain bridge only to place its pixels in a Windows Composition
tree. This lets the OS compositor supply the pixels behind the window without
desktop capture. D3D11 is intentionally used for this bridge: Composition
consumes a DXGI surface, and D3D11 has the smaller interop and synchronization
surface. A future D3D12 renderer remains independent of this choice.
GlassMaterial separates the two ownership models. eSystemGlass is the only
material accepted by SystemBackdropRegion; it builds one shaped HostBackdrop
blur/saturation/tint graph with rectangle, per-corner rounded rectangle,
squircle, notch, or ellipse clipping. eLiquid is represented separately by
LiquidGlassComponent and is never forwarded to the OS compositor. The engine
renderer backend and the reserved eMacOSNative backend currently report
unavailable, so an eLiquid request safely leaves the entity's normal
translucent paint visible. On non-Windows systems system-glass regions
resolve to off. Native DWM
Acrylic and Mica providers are also exposed on Windows, but the raw Win32 DWM
API is window-scoped; the engine therefore accepts them only for a full-window
rectangle and rejects requests that could leak outside their requested shape.
Stock buttons, icon buttons, search fields, and navigation clusters expose a
UiGlassOptions glass member. Custom shapes can use the same route directly:
UiGlassOptions glass = ui_system_glass_options(
8.0f,
1.0f,
{ 1.0f, 1.0f, 1.0f, 0.03f });
ui_apply_glass_material(ui, entity, glass);Use ui_liquid_glass_options() for an engine-owned liquid request. On a future
macOS implementation the same call will select eMacOSNative; no macOS Liquid
Glass code is compiled for now.
MinGW builds the reusable engine normally and invokes MSVC only for the small
vibrance_win32_composition.dll and vibrance_win32_interop.dll companions.
Installing the engine places them beside the engine DLL. The exported CMake
package publishes their paths and
vibrance_engine_copy_runtime_companions(target) so downstream applications
need no MSVC discovery or custom build/copy rules.
Compiled third-party code is linked privately into the engine shared library. GLFW support and precompiled engine shaders are part of that same library. Public dependency headers used by the current C++ API (GLM, EnTT, VMA, GLFW, and Vulkan headers) are copied into the SDK and do not leak local source paths through the exported CMake target.
On MinGW, the GCC, standard C++, and threading runtimes are linked statically. The Vulkan loader remains a system/driver dependency by design.
FFmpeg is enabled only when FFMPEG_PATH supplies genuine static archives.
Shared FFmpeg import libraries cannot be embedded in another DLL, so a
shared-only SDK—or a static command-line package containing only ffmpeg.exe—
produces a configure warning and FFmpeg decoding is disabled. The static SDK
must contain headers plus libavformat.a, libavcodec.a, libavutil.a,
libswscale.a, and libswresample.a. Video frames and audio tracks from
supported containers are then decoded inside the engine DLL. Use
FFMPEG_EXTRA_LIBRARIES for any additional libraries required by the chosen
static FFmpeg build.
Because the public interface is C++, build applications with an ABI-compatible compiler and standard library. A future stable cross-toolchain ABI would require a separate C interface or PIMPL-style boundary.
<vibranceUI/ui/resources.h> provides reusable runtime resource discovery.
Applications pass their own name to ui_discover_resource_directories to add
an AppData/XDG user layer over packaged defaults. asset_candidates and
config_candidates expose user-to-packaged validation fallbacks, while the
file/directory layer APIs expose packaged-to-user inputs for merged parsers.
UiResourceChangeTracker detects additions, removals, and modifications for
runtime reload.
Applications may redirect the user layer with VIBRANCE_RESOURCE_ROOT, or
redirect assets and config independently with VIBRANCE_ASSETS_DIR and
VIBRANCE_CONFIG_DIR. The engine does not copy packaged resources into the
user directory.
While implementation is currently somewhat vague, developers and users are welcome to audit the source code's infrastructure. Contribution to document the engine's capabilities and functionalities are always welcome.
Developers can also create pull requests to implement features and fixing bugs or issues found within the source code.