Use when changing the in-world CCTV / "Truman Show" surveillance cameras in Tiny World Builder — render-to-texture security feeds, the black-and-white CRT/VHS monitor shader, camera placement (lobby side-cams, pumpkincam, treecams), subject tracking, or the lobby screen cutting to live feeds.
76
96%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Low-res black-and-white security cameras that watch the lobby and feed both physical monitors and the big lobby presentation screen. They idle-sweep, then pan to look at whoever is MOVING nearby — like the hidden cameras in The Truman Show. Do not mount CCTV or the big screen in non-lobby worlds.
engine/world/62-cctv-truman.js — the core system. IIFE exposing
window.__tinyworldCCTV. 4-space body indent on purpose so the
duplicate-declaration guard in tools/check.js (which only scans 2-space
top-level decls) ignores its locals.engine/world/63-cctv-placement.js — mounts cams + monitors on lobby room
enter, tears down on non-lobby enter or leave. Exposes
window.__tinyworldCCTVPlacement. Lobby detection is
window.__TW_LOBBY_WORLD_SLUG || 'tidewater-bay'.engine/world/58-lobby-presentation.js — the lobby-only big screen; its
tick() cuts between slides and the hottest live feed.scripts/landing-feed.js + styles/landing.css — the public landing-page
live-worlds panel. World rows are buttons: click once to expand an island
CCTV preview (2D canvas from /api/worlds.preview.cells), click again to
collapse. This is a lightweight marketing/front-door CCTV treatment, not a
Three.js render target.engine/world/25-animation-loop-schema.js calls
window.__tinyworldCCTV.tick(t,dt) then window.__tinyworldLobby.tick(t,dt)
before renderScene() so feeds captured this frame appear this frame.PerspectiveCamera + a small WebGLRenderTarget
(FEED_W×FEED_H, 4:3). tick() round-robins captures (CAPTURES_PER_FRAME,
capped at FEED_FPS) using renderer.setRenderTarget(rt); renderer.render(scene, cam)
— always save/restore the previous render target (getRenderTarget()),
and bail if renderer/scene aren't ready.ShaderMaterial (CRT_FRAG): luminance B&W, scanlines,
a rolling interference bar, hash static, vignette, and uSignal dropout that
dissolves toward static. A second canvas texture (tCaption) bakes the camera
name + live date/time + blinking REC and is composited in the shader. The
material is tagged userData.windowLightEffect = true + lightVisual = true
so prepareFadeable() does NOT swap it for a fade material (skill:
tinyworld-render-performance).#include <colorspace_fragment> (r185 output color
space), like every other ShaderMaterial here.setSubjectsProvider(() => [{pos:Vector3, name}]). The room
feeds WS.subjects() (self + peer avatar sprite positions). Cameras + monitors
live under WS.avatarParent() — the SAME local frame as the avatars and lobby
screen — so subject positions need no conversion.aim() scores each in-range subject by prox*0.6 + moved*1.0 (movement dominates) using per-feed last-position memory; it lerps
curLook toward the winner (TRACK_LERP) or idle-sweeps (IDLE_LERP). A
per-feed activity score spikes on motion and decays (ACTIVITY_DECAY).activeFeed(minActivity) / feedsByActivity() let the lobby
screen auto-cut to whichever camera has something happening. glitch(id,amt)
drops uSignal briefly to sell a cut.On lobby enter only (after a 350ms delay so cells + lobby screen exist) it
mounts:
lobby-l / lobby-r — flank both sides of the presentation screen, angled at
the crowd (toward +z).pumpkincam — over the biggest kind:'pumpkin' cell (scans world[][],
sorts by floors).treecam-1/2 — over the tallest kind:'tree' cells.
Monitors stack up the sides of the lobby screen. window.__tinyworldCCTVFeeds
lists mounted feed ids. On non-lobby enter or leave, clear feeds, disable
capture, and remove monitors.build() stashes screenMesh + slideMat. The screen is shown only in the
configured lobby world, not every island. The state machine auto-advances slides
(AUTO_ADVANCE), then after SLIDE_DWELL swaps the screen material to a
monitorMaterialFor(hotFeed) for FEED_DWELL, then back. Manual presenter
go() and hide() snap back to slides. New API: tick, setCycle, showSlides, showFeed, liveFeed.
?cctv=demo (or =1) drops 4 monitors around origin watching a bobbing test
subject and self-drives the tick — verifies the CRT look + tracking without a
multiplayer room.scripts/landing-feed.js: if local /api/worlds is not
available, mock window.fetch('/api/worlds'), append a fresh copy of the
script, click .hero-feed-link, and assert .hero-feed.is-expanded, one
.hero-feed-cctv-canvas, and the .hero-feed-cctv-meta status/link render.
The panel should anchor below the nav (.hero-feed.is-expanded) and scroll
internally instead of overflowing the hero.addCamera + tick a few
frames with a moving subject, assert activeFeed() returns the feed and
activity > 0.npm run check (duplicate-decl + i18n), npm run smoke, ./publish.sh.renderer.render passes tank FPS (the renderer contract is single-pass per
feed; this is the only sanctioned extra-RT path here besides pixelation).windowLightEffect tag.avatarParent(), not scene, or they
won't inherit the tinyverse scale/offset and subject tracking will be wrong.039a20a
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.