🎨

React で macOS のライブ壁紙を作れるツールを開発した

に公開

デスクトップの壁紙、ずっと静止画のままじゃないですか

自分はずっとそうだった。macOS のデフォルト壁紙を設定して、それっきり。

でもふと思った。React で書いた UI をそのままデスクトップ壁紙にできたら面白くないか? 音楽に反応するビジュアライザーとか、CPU 使用率をリアルタイムに映すシステムモニターとか。

思いついたら作るしかない。そうしてできたのが Fluxlay だ。

https://fluxlay.com

React コンポーネントを書くだけで、macOS のデスクトップ壁紙として動く。デスクトップアプリ・CLI・SDK・ギャラリー(マーケットプレイス)まで一通り作った。

実際のデスクトップで Audio Visualizer 壁紙が動いている様子
Audio Visualizer 壁紙。音楽に合わせてリアルタイムに反応する

何ができるか

いつもの React で壁紙が書ける

普段の開発と全く同じ。JSX で UI を組んで、CSS でスタイリングして、hooks でリアルタイムデータを扱う。

wallpaper.tsx
import { useAudio } from "@fluxlay/react";

export default function AudioVisualizer() {
  const { spectrum } = useAudio();

  return (
    <div className="visualizer">
      {spectrum.map((value, i) => (
        <div
          key={i}
          className="bar"
          style={{ height: `${value * 100}%` }}
        />
      ))}
    </div>
  );
}

たったこれだけで、音楽に反応するオーディオビジュアライザー壁紙になる。

SDK の hooks が結構充実している

Fluxlay の React SDK は、デスクトップ環境のデータに hooks 経由でアクセスできるようにしている。

Hook できること
useAudio() 周波数スペクトラム(32バンド)、RMS、ピーク
useSystemMonitor() CPU・メモリ・バッテリー・ネットワーク等
useMousePosition() グローバルなマウスカーソル位置
useMediaMetadata() 再生中の曲名・アーティスト・アートワーク
useShell() 宣言したシェルコマンドの実行結果
useTerminal() 壁紙の中にターミナルを埋め込める

useSystemMonitor() で CPU 使用率をリアルタイム表示するシステムモニター壁紙、useMediaMetadata() で今聴いている曲のジャケットを壁紙に出す、みたいな使い方ができる。

ギャラリーに公開されている壁紙
現在ギャラリーに公開されている壁紙たち。Audio Visualizer と System Monitor が個人的にお気に入り

CLI で開発してそのまま公開

# 開発サーバー起動(ホットリロード付き)
fluxlay dev ./my-wallpaper

# ビルドして公開
fluxlay build ./my-wallpaper
fluxlay publish ./my-wallpaper

fluxlay dev を叩くと、コードの変更が即座にデスクトップの壁紙に反映される。この開発体験はかなり気持ちいい。

中身の話

ここからは技術的に面白かった部分を書いていく。

全体構成

やっていることはシンプルで、Rust が OS のネイティブ API を叩いてデータを集め、React がそれを描画する。この分離がうまくハマった。

なぜ Electron ではなく Tauri か

壁紙は常時バックグラウンドで動き続ける。ここが普通のデスクトップアプリとの大きな違いで、リソース消費が少ないことが死活問題になる。

Electron だとバイナリだけで 200MB 超え、メモリもそれなりに食う。壁紙のためにそれは重すぎる。

Tauri 2 にした決め手は以下の通り。

  • 軽量: バイナリサイズ・メモリ消費ともに Electron とは比べものにならない
  • Rust で書ける: オーディオ FFT やシステム情報取得みたいなパフォーマンスクリティカルな処理を安全に書ける
  • macOS ネイティブ API に直接アクセスできる: ScreenCaptureKit、CoreGraphics など

壁紙をデスクトップに表示する仕組み

これが一番悩んだところ。macOS で「ウィンドウをデスクトップアイコンの背面に配置する」のは、普通にやると実現できない。

tauri-plugin-desktop-underlay を利用して、通常のウィンドウをデスクトップレイヤーに配置している。全画面で壁紙として描画する仕組みだ。

NDJSON ストリームでリアルタイム通信

SDK と Tauri バックエンド間の通信には HTTP + NDJSON(改行区切り JSON)ストリーム を使っている。

React (useAudio) → HTTP GET /v1/audio-stream
                 ← { "spectrum": [...], "rms": 0.42, "peak": 0.87 }\n
                 ← { "spectrum": [...], "rms": 0.38, "peak": 0.91 }\n
                 ← ...

WebSocket も考えたが、Tauri に内蔵されている Axum サーバーとの相性が良く、curl で叩いてデバッグできる手軽さから NDJSON にした。結果的にこの判断は正解だった。

Rust でリアルタイム FFT

音楽に反応する壁紙を作るには、リアルタイムでオーディオデータを解析する必要がある。ここは Rust の出番。

  1. macOS のシステム音声出力をキャプチャ
  2. rustfft で高速フーリエ変換
  3. 対数スケールで 32 バンドにリサンプリング
  4. RMS(平均音量)とピーク値を算出

対数スケールにしているのは、人間の耳が低音域に敏感だから。線形だと高音域ばかり目立ってしまって、見た目が全然よくない。この調整だけで Audio Visualizer の体験が劇的に変わった。

壁紙プロジェクトの構成

壁紙は fluxlay.yaml 1ファイルで定義する。

fluxlay.yaml
name: "audio-visualizer"
version: "1.0.0"
description: "音楽に反応するオーディオビジュアライザー"

シェルコマンドの宣言もここで行う。ビルドすると .fluxlay 形式にパッケージされて、ギャラリーに公開できる。

技術スタック

レイヤー 技術
デスクトップアプリ Tauri 2 + Rust
壁紙 UI React 19
SDK @fluxlay/react(hooks ベース)
CLI Node.js
ネイティブモジュール Rust + NAPI-RS
Web サイト TanStack Start + Cloudflare Workers
API Go + ConnectRPC

これから

正直、まだまだやりたいことだらけだ。

  • パフォーマンスの最適化: 常時起動するアプリなので、CPU・メモリ消費は1%でも削りたい。特にアニメーションが重い壁紙を複数モニターで動かしたときの負荷はまだ改善の余地がある
  • Windows 対応: 現在は macOS のみ。デスクトップレイヤーの扱いが OS ごとに全く違うので、ここが一番大変そう
  • SDK の拡充: Unity の WebGL ビルドを壁紙として動かせるようにするなど、Web 技術以外の表現も取り込みたい

そして一番の課題は、ギャラリーの壁紙がまだ全部自分の作品しかないこと。6つ。全部自分。マーケットプレイスを名乗るにはさすがに寂しい。

おわりに

「React で壁紙を作る」と言うと大体「え?」って顔をされる。ニッチだと思う。

でも、フロントエンド開発者が普段使っている技術でデスクトップをカスタマイズできるのは、思った以上に楽しい。useAudio の戻り値を divheight に突っ込むだけで壁紙が音楽に反応し始める瞬間は、正直かなりテンションが上がる。

SDK と CLI は公開済みなので、React が書ける人ならすぐに壁紙を作って公開できる。「こういう壁紙あったら面白そう」と思った方、ぜひ作ってみてほしい。ギャラリーに自分以外の壁紙が並ぶ日を本気で楽しみにしている。

https://fluxlay.com

フィードバックもらえると泣いて喜びます。

GitHubで編集を提案
Fluxlay

Discussion