{
	"version": "https://jsonfeed.org/version/1.1",
	"title": "OPUS · notes",
	"home_page_url": "https://opus.ro",
	"feed_url": "https://opus.ro/feed.json",
	"description": "Notes from OPUS: releases, decisions, letters and essays.",
	"language": "en",
	"authors": [
		{
			"name": "OPUS",
			"url": "https://opus.ro"
		}
	],
	"items": [
		{
			"id": "https://opus.ro/notes/2026/how-the-wheel-feels/",
			"url": "https://opus.ro/notes/2026/how-the-wheel-feels/",
			"title": "How the wheel feels",
			"summary": "The duration wheel is the most touched control in Loop. Notes on making it feel like a mechanical detent wheel: what clicks, how hard, and how it lands.",
			"content_html": "<p>The first thing you touch in Loop is the wheel. Before a sound, before a\nsession, you set a length, and you do that by flicking a column of numbers\nuntil the one you want sits in the middle. It is a small control. It is also\nthe one people use every single time, so it is where the app’s character lives\nor does not.</p>\n<p>We wanted it to feel like a good mechanical detent wheel: something with real\nphysics that you might fidget with for the pleasure of it. Getting there turned\nout to be mostly a matter of what not to do.</p>\n<h2 id=\"let-the-system-own-the-physics\">Let the system own the physics</h2>\n<p>An earlier version guessed when your flick had stopped and then snapped to the\nnearest value on a timer. It never landed quite right, because a guess about\ninertia is always a little late. The wheel is now built on the phone’s own\nscroll stack, the same deceleration and snapping the Photos grid uses, aligned\nto the exact centre of each row. There is no timer. When the wheel lands, it\nlands because the scroll did.</p>\n<h2 id=\"weld-the-picture-to-your-thumb\">Weld the picture to your thumb</h2>\n<p>The number in the middle is large and fully opaque. Its neighbours shrink and\nfade. That used to be animated: the app noticed the selection had changed and\neased the sizes over a fraction of a second, which meant the picture always\ntrailed your finger by that fraction. Now every pixel of scroll maps directly\nto a size and an opacity. Nothing eases, nothing lags. The face is a function of\nwhere your thumb is, and the numbers roll like an odometer, measured fresh each\nframe from the row nearest the centre so they land exactly on a whole number\nand never drift during a long spin.</p>\n<h2 id=\"click-and-vary-the-click\">Click, and vary the click</h2>\n<p>Each value that crosses the centre fires a small haptic. On hardware that\nsupports it we use Core Haptics rather than the standard impact generator,\nbecause Core Haptics lets us set two things per click: how hard, and how crisp.</p>\n<p>A slow, deliberate turn, one value at a time, gets a firm and slightly rounded\nclick: intensity 0.62, sharpness 0.52, on a scale of one. A fast spin gets a\nlighter, crisper one, 0.26 and 0.92, so a flurry of values reads as a flurry\nrather than a jackhammer. Between the two extremes the click follows your speed\ncontinuously, reaching the fast end at about 2,200 points per second.</p>\n<h2 id=\"land-differently-from-passing-through\">Land differently from passing through</h2>\n<p>When the wheel comes to rest after a throw there is one more haptic, a touch\nstronger and much rounder than a click: 0.78 and 0.34. It says “landed” rather\nthan “another value”. The two ends of the range get a firmer bump of their own\nso you feel the edges instead of watching for them, and the infinity value at\nthe top, the one for sessions with no end, gets the softest and roundest\nsignature of all.</p>\n<h2 id=\"prepare-before-the-first-touch\">Prepare before the first touch</h2>\n<p>Creating a haptic engine on the first click is the classic latency mistake: the\nclick arrives late and everything after it feels cheap. The engine and its\nfallbacks are warmed up when the wheel appears, so the first click is as good\nas the hundredth.</p>\n<h2 id=\"respect-the-person-who-turned-it-off\">Respect the person who turned it off</h2>\n<p>The system’s haptics switch already governs the standard generators, but Core\nHaptics ignores it. So the wheel treats Reduce Motion as its opt-out: turn that\non and the wheel goes quiet under your thumb, and the roll becomes a plain\nfade.</p>\n<h2 id=\"two-more-small-things\">Two more small things</h2>\n<p>A horizontal swipe across the wheel changes presets. While that swipe is\nhappening the wheel keeps scrolling, because stopping it mid-gesture hitches,\nbut it stops writing values and stops clicking, so an accidental vertical drift\ncannot change your duration.</p>\n<p>And the wheel grows with your text size, up to a point. The face scales with\nDynamic Type; the invisible rows underneath, the snapping and the haptics stay\nexactly where they were.</p>\n<p>None of this shows in a screenshot. It is the part of a tool you only notice\nwhen it is missing, and it is where we spent the most time. If you have Loop,\nturn the wheel slowly, then fast, then let it go. That is the whole note.</p>\n",
			"date_published": "2026-09-05T00:00:00.000Z",
			"tags": [
				"essay",
				"loop"
			]
		},
		{
			"id": "https://opus.ro/notes/2026/loop-1-0-2/",
			"url": "https://opus.ro/notes/2026/loop-1-0-2/",
			"title": "Loop 1.0.2: a new wheel, a better Watch",
			"summary": "The first update since launch. A rebuilt timer wheel, bells you can feel on your wrist, sessions that start on the Watch and sync back later, and a long list of things that now behave.",
			"content_html": "<p>Loop 1.0.2 is in the App Store. It is the first update since launch and most\nof it is under the surface: the connection between iPhone and Apple Watch was\nrebuilt from the ground up, and the things you touch most often were retuned.</p>\n<h2 id=\"what-is-new\">What is new</h2>\n<p><strong>Bells on the wrist.</strong> With an Apple Watch, the start of a session, each\ninterval and the end arrive as a tap on the wrist as well as a sound. There is\nalso a Vibration Only bell for when a sound is not welcome.</p>\n<p><strong>Sessions that start on the Watch.</strong> You can begin a session on the Watch\nwhile the iPhone is somewhere else. It syncs back when the two meet again.</p>\n<p><strong>A new timer wheel.</strong> Rebuilt on the phone’s own scrolling with Core Haptics,\nand a rolling odometer face. <a href=\"https://opus.ro/notes/2026/how-the-wheel-feels/\">We wrote about how it feels</a>.</p>\n<p><strong>Cover Flow for your soundtracks.</strong> Hold to lift and reorder, tap to explore\nthe built-in cover variants.</p>\n<p><strong>Small things.</strong> Mute from the Lock Screen and the Dynamic Island. Swipe the\nwheel sideways to change presets. Keep the screen awake during a session. Edit\nthe current preset from the Watch.</p>\n<h2 id=\"what-changed\">What changed</h2>\n<p>One bell per preset, with a new set of bell sounds. The procedural sounds were\nretuned: warmer noise, retuned binaural beats, and everything loudness matched\nso switching between them does not surprise you. The icon set was refreshed and\nthe settings screens flattened. Profile metrics now default to All Time.</p>\n<h2 id=\"what-was-fixed\">What was fixed</h2>\n<p>The paywall price would not load for some people. Fixed, and the way the app\ndecides whether a purchase exists is now one deterministic check, so people\nwho bought the original paid version are no longer locked out on a new device.</p>\n<p>The Dynamic Island no longer keeps counting after another app interrupts your\naudio, or after a force quit, and its controls work after a cold launch.</p>\n<p>Guards against iCloud data loss across sessions, presets and soundtracks. Many\nWatch sync fixes. Per-preset reminders that are genuinely per preset.</p>\n<p>If something is still not behaving, write to <a href=\"mailto:hello@opus.ro\">hello@opus.ro</a>.</p>\n",
			"date_published": "2026-06-16T00:00:00.000Z",
			"tags": [
				"release",
				"loop"
			]
		},
		{
			"id": "https://opus.ro/notes/2026/ten-principles/",
			"url": "https://opus.ro/notes/2026/ten-principles/",
			"title": "Ten principles",
			"summary": "Written in January 2026, before Loop shipped. Kept as it was, as a record of what the studio thought it was setting out to do.",
			"content_html": "<p class=\"preface\">Written in January 2026, before Loop shipped, and kept here\nas it was. We have since come to prefer letting the tools speak. This stays as\na dated record of what we thought we were setting out to do.</p>\n<p>These are the principles we return to when we’re stuck, when we disagree, when\nthe easy path and the right path diverge. They’re not rules. They’re compass\nheadings.</p>\n<h2 id=\"i-respect-attention\">I. Respect attention</h2>\n<p>Attention is the most valuable thing a person can give you. Treat it\naccordingly. Never take more than you need. Never manufacture urgency. Never\nexploit psychology for engagement.</p>\n<h2 id=\"ii-ship-less\">II. Ship less</h2>\n<p>The feature you don’t build can’t break, can’t confuse, can’t bloat. Every\naddition should justify its existence against the alternative of <em>nothing</em>.\nMost of the time, nothing wins.</p>\n<h2 id=\"iii-charge-honestly\">III. Charge honestly</h2>\n<p>If the product is free, the user is the product. We charge money because it\naligns our incentives with our users’. We succeed when they’re happy, not when\nthey’re addicted.</p>\n<h2 id=\"iv-design-for-the-worst-moment\">IV. Design for the worst moment</h2>\n<p>Don’t design for the user who’s alert, focused, and patient. Design for the\nuser who’s exhausted, anxious, and has three seconds of willpower left. If it\nworks for them, it works for everyone.</p>\n<h2 id=\"v-no-dark-patterns\">V. No dark patterns</h2>\n<p>No trick questions in modals. No pre-checked boxes. No “Are you sure you want\nto miss out?” copy. No hiding the unsubscribe button. No guilt. No manipulation.\nEver.</p>\n<h2 id=\"vi-own-the-whole-stack\">VI. Own the whole stack</h2>\n<p>Understand every layer of what you build. Don’t outsource your core to a\nlibrary you haven’t read. Dependencies are fine. Ignorance isn’t.</p>\n<h2 id=\"vii-prefer-deletion-to-addition\">VII. Prefer deletion to addition</h2>\n<p>When something isn’t working, the first instinct should be to remove, not to\nadd. Complexity is a ratchet. It only goes one direction unless you actively\nfight it.</p>\n<blockquote>\n<p>Perfection is achieved, not when there is nothing more to add, but when there\nis nothing left to take away.</p>\n<p>Antoine de Saint-Exupéry</p>\n</blockquote>\n<h2 id=\"viii-test-on-yourself\">VIII. Test on yourself</h2>\n<p>Use your own products. Daily. In real conditions. Not in a demo environment\nwith perfect data and good lighting. Use them tired, distracted, in bad\nlighting, on a slow connection. That’s where the truth lives.</p>\n<h2 id=\"ix-write-it-down\">IX. Write it down</h2>\n<p>If a decision isn’t documented, it wasn’t made. It was assumed. Assumptions\nrot. Write down <em>why</em> you made a choice, not just <em>what</em> you chose. Your future\nself will thank you.</p>\n<h2 id=\"x-stay-small\">X. Stay small</h2>\n<p>Growth for growth’s sake is the ideology of the cancer cell. Stay small enough\nthat every person on the team knows every product, every user complaint, every\nline of code. Scale the impact, not the headcount.</p>\n<p>These principles aren’t original. Most good ideas aren’t. But writing them\ndown, committing to them publicly, makes them harder to quietly abandon when\nthe pressure mounts.</p>\n<p>And the pressure always mounts.</p>\n",
			"date_published": "2026-01-15T00:00:00.000Z",
			"tags": [
				"essay"
			]
		}
	]
}