RemoteSurface
a tunable aurora served to your phone: a RemoteInspector on the extension seam starts a local-network server, so every @Param appears as a touch control in any browser on the same Wi-Fi, and changes travel live in both directions (RemoteInspector)
- Run
swift run --package-path Examples Example-Integration-RemoteSurface- Imports
OllinRemote- Source
- Examples/Integration/RemoteSurface/Sketch.swift
import Foundation
import Ollin
import OllinRemote
/// A tunable aurora, served to your phone. The sketch registers a
/// `RemoteInspector` on the extension seam, which starts a small local-network
/// server: open the address it prints (and draws, bottom of the canvas) in any
/// browser on the same Wi-Fi and every `@Param` below appears as a touch
/// control, live both ways. Drag a phone slider and the ribbons answer;
/// change a parameter in the host inspector and the phone follows.
///
/// The parameters cover every control family the surface renders: sliders, a
/// toggle, a menu, a color, an XY pad, and a stepper, in three groups.
@main
final class RemoteSurface: Sketch {
enum Palette: String, CaseIterable, ParamOption { case dawn, dusk, noir }
@Param(0.1...4.0, group: "Motion") var speed = 1.4
@Param(0...1, group: "Motion") var turbulence = 0.32
@Param(group: "Motion") var trails = true
@Param(group: "Look") var palette = Palette.dusk
@Param(group: "Look") var accent = Color(red: 1.0, green: 0.22, blue: 0.37)
@Param(0...1, group: "Look") var glow = 0.45
@Param(x: 0...1, y: 0...1, style: .pad, group: "Field") var focus = Vector2(0.5, 0.38)
@Param(1...12, group: "Field") var layers = 6
let remote = RemoteInspector()
override func setup() {
extend(remote)
}
override func draw() {
// Trails keep a translucent wash of the previous frames; off wipes clean.
if trails {
fill(base.withAlpha(0.16))
drawRect(0, 0, width, height)
} else {
background(base)
}
let cx = focus.x * width
let cy = focus.y * height
noFill()
for layer in 0..<max(1, layers) {
let phase = Double(layer) / Double(max(1, layers))
var points: [Vector2] = []
let steps = 90
for i in 0...steps {
let t = Double(i) / Double(steps)
let x = t * width
let wobble = sin(t * 7 + time * speed + phase * 9)
+ turbulence * 1.8 * sin(t * 23 + time * speed * 1.7 + phase * 31)
let spread = 40 + phase * 190
points.append(Vector2(x, cy + wobble * spread + (t - 0.5) * (x - cx) * 0.12))
}
let ink = accent.mixed(with: layerTint, phase * 0.8)
// A wide translucent pass underneath is the glow; the crisp line is drawn over it.
if glow > 0.01 {
stroke(ink.withAlpha(0.10 + glow * 0.16))
strokeWeight(10 + glow * 22)
drawPolyline(points)
}
stroke(ink.withAlpha(0.85))
strokeWeight(1.6)
drawPolyline(points)
}
// The pairing address, on the canvas itself, so the piece tells you
// how to reach it from where you stand.
fill(Color(white: 1).withAlpha(0.55))
textSize(20)
textAlign(.center)
drawText(remote.url ?? "starting the remote surface", width / 2, height - 40)
}
var base: Color {
switch palette {
case .dawn: return Color(red: 0.13, green: 0.09, blue: 0.16)
case .dusk: return Color(red: 0.05, green: 0.06, blue: 0.12)
case .noir: return Color(white: 0.04)
}
}
var layerTint: Color {
switch palette {
case .dawn: return Color(red: 1.0, green: 0.62, blue: 0.30)
case .dusk: return Color(red: 0.36, green: 0.55, blue: 1.0)
case .noir: return Color(white: 0.92)
}
}
}