イロウツシ
気分を言葉で選ばせないアプリ。水面に色を落として指でかき混ぜると、そのときの手の動きがそのまま1枚の絵として残ります。
App Store で配信中できること
- 色を落として、かき混ぜる … 6色のパレットから最大5色を水面に落とし、指でかき混ぜて1枚の絵にします。「落ち着く色」「鮮やかな色」の2つのパレットは、色を落としたあとでも切り替えられます。
- 言葉で分類しない … 気分を選ぶ選択肢や、あとから付ける名前はありません。残るのは色と指の動きの痕跡だけです。
- 記録は最大60秒 … 終わりが近いことは波紋・発光・音で知らせます。振動で急かすことはしません。
- 動きを4つの指標にする … 速さ(瞬間速度の中央値)、広がり(重心からの平均距離)、変化(方向転換の頻度と速度のばらつき)、緩急。結果画面では既定で隠してあります。分類しないアプリが点数を先に見せない、という判断です。
- あとから再生する … 保存したタッチ列から水面を組み立て直して再生します。1×/2×の速度と、前後へのシークに対応。指の軌跡とタップ位置を水面に重ねて表示できます。
- 書き出しと共有 … 音声付きの30fps MP4 と PNG で書き出せます。履歴は上下スワイプでめくれて、まとめて削除もできます。
- 音はアプリの中で作る … タップした位置が五音音階の音に、動きの速さが音量と倍音の明るさに変わります。音色はやわらかい鍵盤・カリンバ・ハープ・ストリングパッド・グラスハーモニカの5種類です。
- 見やすさと10言語 … 弱視の方を主な想定にしていて、Dynamic Type・VoiceOver・「コントラストを上げる」「透明度を下げる」「視差効果を減らす」に対応しています。日本語・英語・スペイン語・フランス語・韓国語・繁体字中国語・ポルトガル語(ブラジル)・ドイツ語・イタリア語・ベトナム語で使えます。
つくり
| アプリ本体 | Swift + SwiftUI。Swift Playgrounds の App Package として組んでいて、外部パッケージへの依存はありません。 |
|---|---|
| 水面 | Metal で書いた流体シミュレーション。速度場、圧力投影、移流、渦度補強、顔料の混合をすべて GPU で処理しています。 |
| 顔料 | ラグランジュ粒子として持ち、Beer–Lambert 則の減算混合で色を重ねます。粒子なので、時間が経っても拡散でぼやけません。 |
| 見た目 | 水面の高さ場から解析的に求めた法線で屈折とスペキュラを描き、粒状ノイズと、バンディングを避けるディザを重ねています。 |
| 入力 | UIKit の生イベントから取得し、1フレーム内の coalesced touches をすべて流体へ渡します。5本同時のマルチタッチを、指ごとに独立したストロークとして記録・再生・数値化します。 |
| 記録の保存 | 履歴一覧の JSON には軽い情報だけを置き、画像は PNG 1枚、タッチ列は固定幅バイナリを LZFSE で圧縮して、記録ごとのディレクトリへ分けています。 |
| 再生・書き出し | 保存した step からシミュレーションを回し直して水面を再構築します。MP4 は画面収録ではなく、保存 step からフレームを合成し直して 30fps で書き出しています。 |
| 音 | 効果音はアプリ内で合成。五音音階で16声まで同時に鳴ります。音源ファイルは持っていません。 |
| セルフチェック | XCTest を持てない環境なので、デバッグ起動時に走る自前の検査(SelfChecks.swift)で代用しています。 |
| 暗号 | 一切使っていません(export compliance の回答を同梱しています)。 |
技術的におもしろいところ
通り過ぎたあとも、尾が巻き続ける
移流に MacCormack 補正を入れてあります。単純なセミラグランジュ移流だと消えてしまう細い渦が、これで残ります。 指は「押す」のではなく、ドラッグの方向に沿って逆回転の渦の対を落としているので、指が通り過ぎたあとも水がしばらく巻き続けます。 減衰や追従の係数はすべて「毎秒」で表現してあり、60Hz の端末でも 120Hz の端末でも同じ動きになります。
「見える波」と「動かす波」を分ける
波紋の見た目の強さと、顔料に与える作用を別のパラメータにしています。しかも波紋は、顔料の速度ではなく位置を直接動かします。 顔料の速度は毎フレーム水流の速度へ引き戻されていて、濃いところでは時定数がおよそ 0.36 秒。波紋の寿命はそれより短いので、 速度を足す書き方では波紋が顔料をほとんど動かせない、と実測で分かったためです。見た目の調整と手応えの調整が、互いを壊さなくなりました。
顔料どうしを引き合わせないことにした
以前は顔料が集まろうとする「凝集」の力を入れていましたが、落ち着き先が丸い塊しかなく、時間が経つと水面が不透明な円で埋まって二度とほどけませんでした。 いまは水彩の縁だまりを、顔料を動かさずに再現しています。顔料の濃度場は同じまま、そこから水面パスの陰影として縁を描くので、絵が固まってしまう副作用がありません。
同じ記録は、何度再生しても同じ絵になる
シミュレーションを毎秒60 step に固定し、UIKit の入力を消費した実 step 番号、助走の step 数、Float32 の時刻と UInt16 の座標を記録の脇に保存しています。
再生は壁時計の時刻ではなく、この保存した step を優先します。速度は UITouch.timestamp 基準です(同じフレームの中の指の動きは、壁時計では区別できないため)。
予測タッチは速度の推定にだけ使い、顔料の位置には反映しません。
そのうえで、保存された simulationVersion を SimulationEngineRegistry が対応するエンジンへ明示的に振り分けます。
version 6 は定数も時計もインクの伝達率も含めて凍結してあり、知らない将来の version を黙って最新のシミュレーションへ流すことはしません。
古い記録の絵が、アプリの更新で作り変わってしまわないようにするためです。
点数を、先に見せない
4つの指標は上限で切り落とさず、100 へ漸近する曲線に載せています。一度速く動かしただけで満点にはなりません。 そのうえで、結果画面では既定で隠してあります。言葉で感情を分類しないアプリが数字を先に出すと、 結局その数字が新しいラベルになってしまうためです。
プライバシー・お問い合わせ
イロウツシは、記録を端末の中だけに保存します。アカウント登録・広告・アクセス解析・利用者を追跡する機能はありません。 くわしくはイロウツシ プライバシーポリシーをご覧ください。
ご意見・不具合のご報告は、Sizu dev のお問い合わせページからご連絡いただけます。