IRO UTSUSHI
지금의 기분에 이름을 붙이라고 하지 않는 앱입니다. 수면에 색을 떨어뜨리고 손가락으로 저으면, 그때의 손놀림이 그대로 한 장의 그림으로 남습니다.
App Store에서 이용 가능할 수 있는 것
- 색을 떨어뜨리고 젓기 … 6가지 색 팔레트에서 최대 5가지를 수면에 떨어뜨리고, 손가락으로 저어 한 장의 그림으로 만듭니다. 「차분한 색」과 「선명한 색」 두 팔레트는 색을 떨어뜨린 뒤에도 바꿀 수 있습니다.
- 말로 분류하지 않기 … 기분을 고르는 선택지도, 나중에 붙이는 이름표도 없습니다. 남는 것은 색과 손가락 움직임의 흔적뿐입니다.
- 기록은 최대 60초 … 끝이 가까워지면 물결과 빛, 소리로 알려 줍니다. 진동으로 재촉하지는 않습니다.
- 움직임을 네 가지 지표로 … 빠르기(순간 속도의 중앙값), 퍼짐(무게중심으로부터의 평균 거리), 변화(방향 전환의 빈도와 속도의 편차), 완급. 결과 화면에서는 기본적으로 숨겨 둡니다. 분류하지 않는 앱이 점수부터 보여 주지 않는다는 판단입니다.
- 나중에 다시 재생하기 … 저장된 터치 열에서 수면을 다시 만들어 재생합니다. 1×/2× 속도와 앞뒤 탐색을 지원하고, 손가락의 궤적과 탭한 위치를 수면 위에 겹쳐 볼 수 있습니다.
- 내보내기와 공유 … 소리가 들어간 30 fps MP4와 PNG로 내보낼 수 있습니다. 기록은 위아래로 쓸어 넘기고, 여러 개를 한 번에 삭제할 수도 있습니다.
- 소리는 앱 안에서 만듭니다 … 탭한 위치는 5음 음계의 음이 되고, 움직임의 빠르기는 음량과 배음의 밝기가 됩니다. 음색은 부드러운 건반·칼림바·하프·스트링 패드·글라스 하모니카 다섯 가지입니다.
- 접근성과 10개 언어 … 저시력 사용자를 주로 염두에 두고 만들었으며, 동적 크기(Dynamic Type)·VoiceOver·「대비 증가」·「투명도 감소」·「동작 줄이기」를 지원합니다. 일본어·영어·스페인어·프랑스어·한국어·번체 중국어·포르투갈어(브라질)·독일어·이탈리아어·베트남어로 이용할 수 있습니다.
만듦새
| 앱 본체 | Swift + SwiftUI. Swift Playgrounds의 App Package로 만들었으며, 외부 패키지 의존성은 하나도 없습니다. |
|---|---|
| 수면 | Metal로 작성한 유체 시뮬레이션. 속도장, 압력 투영, 이류, 와도 보강, 안료 혼합을 모두 GPU에서 처리합니다. |
| 안료 | 라그랑주 입자로 다루고, Beer–Lambert 법칙에 따른 감산 혼합으로 색을 겹칩니다. 입자이기 때문에 시간이 지나도 확산으로 뭉개지지 않습니다. |
| 겉모습 | 수면 높이장에서 해석적으로 구한 법선으로 굴절과 스페큘러를 그리고, 그 위에 입자 노이즈와 밴딩을 피하기 위한 디더를 얹습니다. |
| 입력 | UIKit의 원시 이벤트에서 받아, 한 프레임 안의 coalesced touches를 모두 유체로 넘깁니다. 최대 5개의 동시 터치를 손가락별로 독립된 스트로크로 기록·재생·수치화합니다. |
| 기록의 저장 | 기록 목록 JSON에는 가벼운 정보만 담고, 이미지는 PNG 한 장, 터치 열은 고정 폭 바이너리를 LZFSE로 압축해 기록마다 별도 디렉터리에 저장합니다. |
| 재생·내보내기 | 저장된 step에서 시뮬레이션을 다시 돌려 수면을 재구축합니다. MP4는 화면 녹화가 아니라, 저장된 step에서 프레임을 다시 합성해 30 fps로 씁니다. |
| 소리 | 효과음은 앱 안에서 합성합니다. 5음 음계로 최대 16성까지 동시에 울리며, 음원 파일은 두지 않습니다. |
| 셀프 체크 | XCTest를 둘 수 없는 환경이라, 디버그 실행 시 돌아가는 자체 점검(SelfChecks.swift)으로 대신합니다. |
| 암호화 | 전혀 사용하지 않습니다(export compliance 답변을 함께 넣어 두었습니다). |
기술적으로 재미있는 부분
손가락이 지나간 뒤에도 꼬리가 계속 말립니다
이류에 MacCormack 보정을 넣었습니다. 단순한 세미라그랑주 이류라면 사라져 버릴 가느다란 소용돌이가 이것으로 남습니다. 손가락은 물을 「미는」 것이 아니라 드래그 방향을 따라 서로 반대로 도는 소용돌이 쌍을 떨어뜨리기 때문에, 손가락이 지나간 뒤에도 물이 한동안 계속 말립니다. 감쇠와 추종 계수는 모두 「초당」으로 표현해 두어서 60 Hz 기기든 120 Hz 기기든 같은 움직임이 됩니다.
「보이는 물결」과 「움직이는 물결」을 나눕니다
물결의 겉보기 세기와 안료에 미치는 작용을 서로 다른 파라미터로 두었습니다. 게다가 물결은 안료의 속도가 아니라 위치를 직접 움직입니다. 안료의 속도는 매 프레임 물살의 속도로 끌려 돌아가는데, 짙은 곳에서는 시간 상수가 약 0.36초입니다. 물결의 수명은 그보다 짧아서, 속도를 더하는 방식으로는 물결이 안료를 거의 움직이지 못한다는 것이 실측으로 드러났습니다. 겉모습을 다듬는 일과 손맛을 다듬는 일이 서로를 망가뜨리지 않게 되었습니다.
안료끼리 서로 끌어당기지 않게 했습니다
예전에는 안료가 모이려는 「응집」 힘이 있었지만, 가라앉을 곳이 둥근 덩어리뿐이라 시간이 지나면 수면이 불투명한 원으로 가득 차 다시는 풀리지 않았습니다. 지금은 수채화의 가장자리 얼룩을 안료를 움직이지 않고 재현합니다. 안료의 농도장은 그대로 두고 거기에서 수면 패스의 음영으로 가장자리를 그리기 때문에, 그림이 굳어 버리는 부작용이 없습니다.
같은 기록은 몇 번을 재생해도 같은 그림이 됩니다
시뮬레이션을 초당 60 step으로 고정하고, UIKit 입력을 소비한 실제 step 번호, 도움닫기 step 수, Float32 시각과 UInt16 좌표를 기록 옆에 함께 저장합니다.
재생은 벽시계 시각보다 이 저장된 step을 우선합니다. 속도는 UITouch.timestamp 기준입니다(같은 프레임 안의 움직임은 벽시계로는 구분할 수 없기 때문입니다).
예측 터치는 속도 추정에만 쓰고, 안료가 놓이는 위치에는 반영하지 않습니다.
그 위에 SimulationEngineRegistry가 저장된 simulationVersion을 대응하는 엔진으로 명시적으로 분배합니다.
version 6은 상수도 시계도 잉크 전달률까지 포함해 동결해 두었고, 알 수 없는 미래의 version을 말없이 최신 시뮬레이션으로 흘려보내지 않습니다.
앱을 업데이트했다고 해서 예전 기록의 그림이 다른 그림으로 다시 만들어지지 않도록 하기 위해서입니다.
점수를 먼저 보여 주지 않습니다
네 가지 지표는 상한에서 잘라 내지 않고 100에 점근하는 곡선에 올려 두었습니다. 한 번 빠르게 움직였다고 만점이 되지는 않습니다. 그러고도 결과 화면에서는 기본적으로 숨겨 둡니다. 감정을 말로 분류하지 않는 앱이 숫자부터 내밀면, 결국 그 숫자가 새로운 이름표가 되어 버리기 때문입니다.
개인정보 · 문의
IRO UTSUSHI는 기록을 단말기 안에만 저장합니다. 계정 등록, 광고, 접속 분석, 이용자를 추적하는 기능은 없습니다. 자세한 내용은 IRO UTSUSHI 개인정보 처리방침을 확인해 주세요.
의견이나 오류 제보는 Sizu dev의 문의 페이지에서도 보내실 수 있습니다.