Durvald

Local-first music player with a shared Rust core and native interfaces

2025

Product and current stage

I am developing Durvald as a local-first music player, moving from a SolidJS/Tauri interface to native applications with a shared Rust core. My goal is to organize and play the listener's collection on their own device.

I have made the most progress on the macOS implementation. Windows is part of the migration to native interfaces, and an initial GTK4 implementation exists for Linux. These clients are at different stages; I do not yet offer feature parity across platforms.

Native Durvald interface on macOS, showing library navigation, the Switched-On Bach album and playback controls.Native macOS version: album details, track list and persistent player.

Authorship and learning

I developed the entire project, from its logo and visual identity to the interfaces and Rust core, with assistance from different AI models. Durvald is a personal development and learning project.

From Tauri to native interfaces

The Tauri version was still incomplete, roughly halfway through development, when I chose native interfaces as an opportunity to learn SwiftUI and GTK4. Much of the Rust core was already used in Tauri, limiting the impact of the change through reuse. The migration took place while building the product, rather than replacing an established production system.

In the first implementation, I combined SolidJS presentation, Tauri commands and events for communication, and Rust processing. I keep its source public in durvald-tauri, documenting the earlier stage of the same product.

In the current architecture, I concentrate shared responsibilities in the UI-independent durvald-core. The product's purpose remains the same, while native clients replace the shared presentation layer. This choice requires maintaining platform-specific integrations and interface behavior. The memory section records a local observation of both versions; controlled benchmarks are outside the current scope.

Interface of the earlier Durvald implementation using SolidJS and Tauri.Screenshot of the SolidJS/Tauri version; it does not show the current native interface.

Visual consistency across systems

The move toward platform-native interfaces also addressed a problem I observed on Windows: transparency and blur effects using backdrop-filter failed in overlapping components of the Tauri interface. I associated this behavior with Chromium issue #40835530, which I used as a technical reference for the investigation.

This experience increased the visual compatibility work required by the shared interface and contributed to my decision to adopt native clients. The reference identifies the investigated problem; I have not confirmed its current resolution status in this review.

Tauri context menu on Windows, with a rectangular background extending beyond its rounded corners.I captured this defect on Windows. I did not observe the same problem on Linux.

Linux distribution and maintenance cost

Beyond the goal of native interfaces, my Linux development workflow depended on an Ubuntu environment. External reports provide context: discussion #12960 records a linuxdeploy failure on Arch after compilation, involving an incompatible strip tool and maintainer comments on glibc portability. Issues #15106 and #11701 also report AppImage generation failures on Arch.

These reports reinforce the need to control packaging environments and distribution dependencies. They do not establish a universal Ubuntu requirement or a controlled comparison between distributions. Moving to GTK4 removes the Linux client's WebView, but system libraries, packaging and compatibility across distributions still require validation.

Shared core and contracts

I centralized the following responsibilities in the Rust core: library indexing, SQLite persistence, local playback, history, settings and Last.fm integration. SwiftUI and other FFI clients use its UniFFI surface; the GTK4 client, also written in Rust, consumes the public Rust API directly without UniFFI bindings.

This boundary keeps shared rules outside the interfaces. The documentation defines contracts for paths and managed artwork, scanning and cancellation, audio formats and public errors. Clients should branch on CoreError variants rather than interpret diagnostic messages as contracts.

Consistency and long-running operations

Only one library scan may run at a time. Cancellation is cooperative: work stops at safe boundaries and cancelled results are not persisted. A complete scan reconciles removed files; a partial or cancelled scan preserves existing records.

In the GTK4 client, widgets remain on the GTK/GLib thread and core operations are dispatched to a Tokio runtime. This separates blocking work from the interface thread and requires coordinating asynchronous results with the window lifecycle.

Implementation by platform

  • macOS: native SwiftUI client integrated through the UniFFI surface; the most advanced development track.
  • Windows: under development as part of the move to a native implementation. I have not yet provided a demonstration or validated feature list for this version.
  • Linux: a Rust/GTK4 foundation that opens a window, initializes the core and reads the number of persisted tracks. Import, track lists and playback controls remain next steps. Refreshing the library rereads the database; it does not start a scan.

Observed idle memory

I transcribed the values below from a local Activity Monitor observation with both versions idle. I identified Durvald as the native version and the other four processes as the Tauri version.

ImplementationProcess or comparisonDisplayed memory
Native macOSDurvald73.5 MB
Taurihttp://localhost:142095.7 MB
Tauridurvald53.2 MB
Tauridurvald Graphics and Media17.3 MB
Tauridurvald Networking10.7 MB
Tauri totalSum of the four processes176.9 MB
Observed differenceTauri minus native103.4 MB (approximately 58% lower for native)

In this comparison, I summed the values displayed by Activity Monitor, not exclusive physical memory. This is a single observation: I did not record hardware, macOS version, commits, build mode, library size or idle duration in this case. The localhost address indicates locally served content in the Tauri version. These values do not establish a general reduction in memory, CPU usage or startup time; a reproducible comparison requires equivalent conditions and multiple runs.

Evidence and limits

In this case, I bring together public code, interface screenshots and a local memory observation. My focus is learning native interface development and reusing the Rust core while building the product. Comparative benchmarks are outside the current scope; I retain the memory values as a single observation rather than a general performance claim.

Current source

Earlier version — Tauri

Rust core contracts

Linux client status and execution