Skip to content

kosail/Aria

Repository files navigation

Aria logo

Nature’s silent symphony.

Aria is an ambient sound app. A minimalist one.

Built from scratch using Compose Multiplatform, Aria is designed to flow across platforms. From Linux to Windows, from web to Android, while honoring the quiet beauty of the original Blanket app, built by Rafael Mardojai for the GNOME desktop.

I wanted to bring peace, calm, and ambient beauty to more people, on more platforms. Blanket is currently only available for Linux, and I wanted to bring it to the rest of the world.

So this is my handcrafted tribute to it, to all the wonderful people who made possible Blanket exist, and thus, made Aria possible too.


🌼 Features

Everything that Blanket already bring to us:

  • A curated library of ambient nature sounds: rain, fire, birds, forest, and more
  • Per-sound volume sliders + master volume control

But also:

  • Built-in timer to gently fade out after a chosen duration (perfect for sleep)
  • Add your own sounds to the library (Android only, due limitation in the browser API for web target)
  • Many languages are supported (English and Spanish are made by me, while Portuguese, Italian, French, German, and Dutch are AI generated)

Current state

  • Web version (WASM/JS): Completed! Already released on GitHub Pages.
  • Android version: Completed! You can download the APK from the releases page.
  • Desktop version (Tauri + WASM/JS): Completed! Yet I think some changes could make it look better; that would be good for a future release.
  • Desktop version (JVM): DEPRECATED. See the deprecation note below. I may give it another try in the future tho...

Screenshots of the nightly version.

Web version Nightly screenshot, dark mode

Desktop & Android version Nightly screenshot, dark mode


🌻 Setup

  • Clone and build the project locally.
git clone https://github.com/kosail/aria.git
cd aria

For WEB builds you can do:

# Local development
./gradlew wasmJsBrowserDevelopmentRun

# Distribution
./gradlew wasmJsBrowserDistribution

For Android, open the project in Android Studio and built it like any other APK. I can't provide much details on this because I use IntelliJ IDEA, which already detects the Android project and just adds the play button to the run configurations.

For Desktop, the JVM target is deprecated and will not work if you compile it from this code. Instead of deploying the JVM target, I used Tauri to build a "native" app (it's native but uses webview). I will be releasing the code to build it soon.


Deprecation note of JVM Version

1. I love the JVM ecosystem, but sadly, it is not enough the correct tool for the job this time.

Finding audio libraries for the JVM that were modern, performant, and compatible with many formats was just... ugh. It was a nightmare. Sure, there might be some out there (and if you know about one please let me know! I will be glad to not let die the Desktop JVM target).

So, in the end I couldn't make javax.sample (the built-in solution for audio) work at all, and I gave up. I end up giving another try to KorGe, even though I knew that it used javax.sample as its backend on JVM. Surprisingly, it worked. Thus, I assumed this whole issue was a skill issue, not a tooling one. Still, there were two big issues using KorGe:

  1. OGG audio files are not supported. Due to limitations inherited from javax.sample, only WAV and another bunch of weird old formats are supported. This could be solved with a decoding lib like libvorbis for OGG and the same for MP3, though it would require a lot of work and introduce many issues due external deps.
  2. The audio files took literally 1–2 seconds to load in the first play per session. This may not sound like a real issue... but I like blazing fast apps. I couldn't help but notice it.

I solved the first issue by just using WAV files instead of OGG. Though, this made the app around 100MB larger in disk space. The second one, however, was a bit more tricky. I found that using the streaming = true flag helped a bit, but just a bit. I tried preloading the files at startup into memory, but loading them into resourcesVfs (KorGe's global memory) was not enough, yet actually started playing them. I noticed that after the first play per session, consecutive ones were much faster. Like, almost instantly. So my workaround was to set all files to streaming and to start them all for an instant. It had no extra startup penalty to the app, as everything is done in asynchronously, but memory usage... hmm, well... 750MB. I hate RAM-heavy apps, but this time I literally did my best trying to find a better solution, but I just couldn't. Audio libs in Java are a thing.

I'm disappointed with myself that I had to give up on the JVM target. However, then I visited Blanket page on GitHub and I saw that someone already did a Windows version of the app, called Blanket+ and there is also a macOS native one, called (Blankie)[https://github.com/codybrom/blankie]. So, I entrust them to keep alive the project in Desktop natively, and I decided to package the Web target I created into Tauri, as a simple solution.


🔧 Stack & Resources

Stack

  • Compose Multiplatform (Android and Web WASM/JS) — UI framework
  • Tauri v2 (Desktop version via WASM/JS) - Backend to run the web app in a native app.
  • Audio library — The web target uses the browser API via kotlinx-browser. On Android, it uses Exoplayer.
  • GitHub Pages — For deployment of the web target

Resources

Show/Hide

-> Icons

-> Fonts

-> Audios

  • All the audios are the original ones used in Blanket. To see more information about the authors and licensing, please check SOUNDS_LICENSING.

💐 Contributing

Contributions are welcome! Feel free to fork the repository and submit pull requests. If you have ideas, suggestions, or bug reports, open an issue on GitHub.

Sound designers: If you have original nature loops and would love to contribute them to Aria, reach out!


🎒 What I have learned so far from this project

  1. I'm still a beginner in Compose Multiplatform, not to say in Kotlin. It's such a difficult language...
  2. LocalCompositions are a very powerful feature. I'm still learning how to use them properly, and I take them similarly as Zustand stores in the React world. Anyway, I love how easy the code becomes when you use them.
  3. Hell, Kotlin-JS wrappers are god-sent but also diabolic to use.
  4. Kotlin async programming is hellish difficult. Like, I felt JS async was difficulty level = ok. But Kotlin? Kotlin's difficulty level = Ninja Gaiden 3. I saw many YouTube videos explaining how to do async in Kotlin since, but I'm still not sure how to do it properly. It made me realize the problem is not Kotlin, it's me. So that is my next bus stop: properly learn async programming.

📜 License

GPLv3 License logo. Copyright © 2012 Christian Cadena

GPLv3 (GNU General Public License v3) – Free to use, modify, and distribute as long as this remains open source.

GPLv3 Logos:

Copyright © 2012 Christian Cadena
Available under the Creative Commons Attribution 3.0 Unported License.

Aria Copyright © 2025-2026, kosail
With love, from Honduras.

About

An ambient sounds app. Nature’s silent symphony.

Resources

License

Stars

11 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages