Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

cap: camera debug telemetry + ?camrot override + restore 'cap' label

To bisect the iPhone rotation bug from afar:
- bios.mjs posts a camera:debug message (requested/actual dims,
trackSettings, facingMode, iOS/Android, screen.orientation.angle,
userAgent) right after getUserMedia succeeds, and a one-shot
camera:debug:frame with the actual video.videoWidth/Height plus
our needsRotation verdict once the first real frame arrives.
- cap.mjs act() console.log()s both, so they land in piece-runs
(MongoDB) and we can read them back with ac-piece-logs.
- Added a URL override: ?camrot=0|cw|ccw|180 sets the rotation
angle manually, and ?camforce=1 forces the rotation path even
when dims-based detection says no. Lets the user quickly try
every 90° option on device without another push cycle.

Also: drop nohud=true from cap.mjs. The corner now shows the
standard 'cap' label again instead of an empty top-left.

+82 -12
+71 -10
system/public/aesthetic.computer/bios.mjs
··· 19894 19894 // const capabilities = videoTrack.getCapabilities(); 19895 19895 settings = videoTrack.getSettings(); 19896 19896 19897 - // console.log( 19898 - // "Got: Width:", 19899 - // settings.width, 19900 - // "Height:", 19901 - // settings.height, 19902 - // ); // ❤️‍🔥 19903 - 19904 19897 // Update global facingMode in case different from requested. 19905 19898 facingMode = videoTrack.getConstraints().facingMode; 19899 + 19900 + // 📊 Send camera debug telemetry to the worker so pieces (e.g. cap) 19901 + // can console.log() it and the piece-runs silo captures it. 19902 + // This lets us diagnose rotation/orientation issues on devices we 19903 + // can't inspect directly. 19904 + const orientationAngle = 19905 + (typeof screen !== "undefined" && screen.orientation?.angle) ?? 19906 + (typeof window !== "undefined" ? window.orientation : null) ?? 19907 + null; 19908 + const isPortrait = 19909 + typeof window !== "undefined" 19910 + ? window.matchMedia?.("(orientation: portrait)")?.matches 19911 + : null; 19912 + send({ 19913 + type: "camera:debug", 19914 + content: { 19915 + requested: { width: reqWidth, height: reqHeight }, 19916 + bufferRequested: { width: cWidth, height: cHeight }, 19917 + trackSettings: { 19918 + width: settings?.width ?? null, 19919 + height: settings?.height ?? null, 19920 + aspectRatio: settings?.aspectRatio ?? null, 19921 + facingMode: settings?.facingMode ?? null, 19922 + frameRate: settings?.frameRate ?? null, 19923 + }, 19924 + facingMode, 19925 + iOS, 19926 + Android, 19927 + isPortrait, 19928 + orientationAngle, 19929 + userAgent: 19930 + typeof navigator !== "undefined" ? navigator.userAgent : null, 19931 + }, 19932 + }); 19906 19933 19907 19934 const devices = await navigator.mediaDevices.enumerateDevices(); 19908 19935 const videoDevices = devices.filter( ··· 20111 20138 video.videoWidth > 0 && 20112 20139 video.videoHeight > 0 && 20113 20140 videoIsLandscape === bufferIsPortrait; 20141 + 20142 + // 📊 One-shot diagnostic: report actual video dimensions once the 20143 + // first real frame is available. Lands in piece-runs via cap.mjs. 20144 + if (!video.__acFrameDebugReported && video.videoWidth > 0) { 20145 + video.__acFrameDebugReported = true; 20146 + send({ 20147 + type: "camera:debug:frame", 20148 + content: { 20149 + video: { width: video.videoWidth, height: video.videoHeight }, 20150 + buffer: { width: buffer.width, height: buffer.height }, 20151 + videoIsLandscape, 20152 + bufferIsPortrait, 20153 + needsRotation, 20154 + facingMode, 20155 + }, 20156 + }); 20157 + } 20114 20158 // Mirror the front camera for selfie framing. Desktop webcams are 20115 20159 // conventionally mirrored too. Mobile rear camera stays unmirrored. 20116 20160 const needsMirror = facingMode === "user" || (!iOS && !Android); ··· 20118 20162 // Front cameras are mounted mirrored relative to rear cameras, so the 20119 20163 // rotation that lands the subject upright is opposite: front = +90° CW, 20120 20164 // rear = -90° CCW. Desktop webcams behave like front cameras. 20121 - const rotationAngle = 20165 + // 🧪 URL override for camera rotation while we bisect the correct 20166 + // angle on device: ?camrot=0|cw|180|ccw (0 = no rotation). Also 20167 + // ?camforce=1 forces the rotation path even when dims say no. 20168 + let rotationAngle = 20122 20169 facingMode === "user" || (!iOS && !Android) 20123 20170 ? Math.PI / 2 20124 20171 : -Math.PI / 2; 20172 + let forceRotation = false; 20173 + if (typeof location !== "undefined" && location.search) { 20174 + const camParams = new URLSearchParams(location.search); 20175 + const camrot = camParams.get("camrot"); 20176 + if (camrot === "cw" || camrot === "90") rotationAngle = Math.PI / 2; 20177 + else if (camrot === "ccw" || camrot === "-90") 20178 + rotationAngle = -Math.PI / 2; 20179 + else if (camrot === "180") rotationAngle = Math.PI; 20180 + else if (camrot === "0" || camrot === "none") rotationAngle = 0; 20181 + if (camParams.get("camforce") === "1") forceRotation = true; 20182 + } 20183 + const needsRotationFinal = forceRotation 20184 + ? video.videoWidth > 0 && video.videoHeight > 0 20185 + : needsRotation; 20125 20186 20126 20187 // Send frames by default (non-rotation path only). 20127 - if (!needsRotation && needsMirror) { 20188 + if ((!needsRotationFinal || rotationAngle === 0) && needsMirror) { 20128 20189 bufferCtx.translate(buffer.width / 2, buffer.height / 2); 20129 20190 const zoom = 1; 20130 20191 // if (hands) { ··· 20156 20217 } 20157 20218 20158 20219 // Drawing a video frame to the buffer (mirrored, proportion adjusted). 20159 - if (needsRotation) { 20220 + if (needsRotationFinal && rotationAngle !== 0) { 20160 20221 // Landscape sensor pixels in portrait buffer: rotate ±90° so the 20161 20222 // video fills the portrait frame. After rotation the effective 20162 20223 // video width/height are swapped, so fit calculation uses the
+11 -2
system/public/aesthetic.computer/disks/cap.mjs
··· 299 299 } 300 300 301 301 function act({ event: e, jump, video, cameras, sound, rec, notice, leaving, hud }) { 302 + // 📊 Camera diagnostics from bios.mjs → piece-runs via console.log. 303 + if (e.is("camera:debug")) { 304 + console.log("📷 cam/init:", JSON.stringify(e)); 305 + } 306 + if (e.is("camera:debug:frame")) { 307 + console.log("📷 cam/frame:", JSON.stringify(e)); 308 + } 309 + 302 310 // Handle microphone connection events 303 311 if (e.is("microphone-connect:success")) { 304 312 micConnected = true; ··· 451 459 452 460 export { boot, paint, sim, act, beat, leave }; 453 461 454 - // No nopaint system - cap records the screen directly via tape infrastructure 455 - export const nohud = true; // Minimal HUD for camera view 462 + // No nopaint system — cap records the screen directly via tape infrastructure. 463 + // We keep the standard HUD corner label ("cap") visible; hiding it (nohud) 464 + // left the corner empty and felt broken.