Skip to content

67js 0.19.0

Choose a tag to compare

@github-actions github-actions released this 12 Nov 03:51
6d83ae4

67js is now Crucible. This release shipped as @codepawlhq/67js, which is deprecated and gets no more updates. Install @codepawlhq/crucible instead and see crucible.codepawl.com and the v0.20.0 notes for the move (bins crucible-mcp, class CrucibleRenderer, skills build-crucible-scene and render-crucible-media).

  • scene_review, scene_render_status and scene_render_result return only what an agent acts on by default; response_format: "detailed" keeps the full diagnostics, digests and metrics, and scene_capabilities takes concise to drop the field notes of a topic (#656).
  • scene_capabilities topic examples (and examples.<tool>) serves one valid call each for scene_create, scene_apply, scene_create_asset and scene_render_submit, with no growth of any tool schema (#657)
  • MCP: one server holds up to 4 open scenes under a 96 MiB geometry cap; the least recently used scene is evicted with a clear error. sessionId (returned by scene_create) picks the scene on every tool, and a call without it uses the last used scene. Each scene keeps 16 checkpoints, oldest evicted first (evictedCheckpointIds), and a render job stays tied to its scene (#658).
  • Motion drivers read custom properties on nodes and bones (motion.properties), distance and rotation difference variables, and can drive lights and shape keys (#694)
  • Mesh particle emitters take from: "edges", with an optional group and sharpAngle, so sparks spread along a rim by edge length (#695)
  • Mesh particle emitters take a vertex group (emitter.group) that weights surface and vertex emission, so sparks come off a painted rim (#695)
  • A cloth sheet with selfCollision: true now reads its own thickness as the contact radius (never past half the shortest edge), so a flag dropped to the floor lies as a thin crumpled layer. Behaviour change: a sheet that sets both selfCollision: true and thickness used to ignore the thickness (radius 0.9 of half the shortest edge) and now keeps layers 2 x thickness apart, so its baked frames differ; pass selfCollision: { radius } to keep a fixed radius. Sheets that set no thickness keep the old radius (#696)
  • A cloth sheet takes softBody (shape matching and pressure on a closed mesh), selfCollision and a stiffnessGroup vertex group, all baked and seekable (#696)
  • Field expressions read face area, edge angle, island index and neighbour counts, and accumulate, statistic, blur, sample index and rank over a whole mesh domain (#699)
  • A recipe curve takes grow, morph targets of the curve grown step by step, so a motion morph track grows a vine and seeking equals playing (#701)
  • mesh_edit gains unsubdivide and circularize, and poke, dual, tris to quads and symmetrize now have Blender golden cases (#703).
  • effects.composite keys a green screen (chroma) or a luma range out of the frame and draws it over a colour or gradient background, with the same mask as the other post steps (part of #707)
  • Post steps take a mask by part names or a depth range, and effects.stylize adds sharpen, kuwahara, posterize and pixelate filters (part of #707)
  • Mesh edits gain geometry masks: vertex_group measure curvature, edge or occlusion, and vertex_color gradients along a group, so a mesh with no UVs can carry edge wear and dirt (#708)
  • Materials gain wear: a vertex group blends the material from paint to bare metal per pixel and a second group adds dirt, so a mesh with no UVs shows metal on its edges and dirt in its cavities (#708)
  • Material graphs get brick, whiteNoise, coord and mapping nodes, so a procedural material works on a mesh without UVs (#708)
  • Blended and transmissive parts cast sun shadows weighted by opacity and tinted by colour, so stained glass, curtains and leaf canopies throw coloured and partial shadows; castShadow: false turns it off (#709)
  • environment.localVolumes adds up to 8 box or sphere volumes that absorb and emit light, in the rasterizer and the CPU path tracer (#710)
  • A mesh boolean cutter can be { "geometry": "<id>" }, another recipe geometry, so cuts form a bounded DAG with cycle and missing-ID errors (#730)
  • Render jobs take a wireframe pass that draws polygon edges over the shaded surface and colours boundary, non-manifold, flipped and degenerate edges for visual review (#732)
  • exportObj (OBJ with MTL text) and exportStl (binary STL) in the new addons/export-mesh entry write the same parts as exportGlb, in world space, with deterministic bytes (#747)
  • The jolt adapter builds convexHull colliders from the flat xyz point array and frees its world when setup throws.
  • 67js/addons/physics-jolt adds joltAdapter, a second engine for the live physics runtime built on the optional jolt-physics package: the same contact and sensor events as Rapier on the same scene, queries, kinematic control and force fields, with recorded 500-box step times for both engines (part of #771)
  • 67js/addons/physics-runtime adds createPhysicsSystem, live physics as a runtime system over an engine adapter (Rapier first): stepped in fixedUpdate, contact and sensor events, kinematic control, ray, shape-cast and overlap queries, and interpolated part matrices (part of #771)
  • createCameraClip treats a ground function that returns a non-finite height as ground height 0, so the camera still stays above the floor instead of clipping being switched off (#772).
  • 67js/addons/player adds vehicle and free-flight controllers, orbit, chase and side-scroller camera rigs, createCameraClip (camera pull-in against walls and ground), and a registry (registerController, registerCameraRig, replaceController, replaceCameraRig) for custom kinds and runtime swaps (#772)
  • Fix: the vehicle and flight controllers ignore a zero-length step, so the runtime's first variable-mode tick no longer turns their speed and velocity into NaN.
  • 67js/addons/player adds a character controller (walk, run, jump, kinematic collide-and-slide on a ground plane, heightfield or boxes, with a mover seam for physics) and swappable follow, first-person and top-down camera rigs as runtime systems (#772)
  • 67js/addons/input adds touchSource (virtual sticks, tap, swipe, pinch), deviceMotionSource, axis smoothing, and recordInput with replaySource so one demo runs from keyboard, gamepad, touch or a replay (#773)
  • 67js/addons/input adds createInput, an open input layer: sources register, named actions and axes with rebinding and dead zones, keyboard, mouse and gamepad sources, and per-fixed-step sampling as a runtime system (#773)
  • Animation graph: blended clips now end with their own shares in poseLayers() (the chain of lerp factors ran in the wrong order, so a walk to run blend played as full run); planted feet no longer slide in a blend space (#774)
  • Breaking for tuned graphs: a layer with two or more active slots (blend space or crossfade) now gives a different pose than before, because the old mix was wrong. Graphs tuned around the old output need a fresh look (#774)
  • createPathFollower walks an NPC along a navmesh route through the animation graph, blending idle, walk and run by speed and moving by root motion (#774)
  • 67js/addons/animation-graph adds createAnimationGraph, a live animation state graph: states with clips or 1D and 2D blend spaces, parameter transitions with crossfades, masked layers, deterministic fixed steps, a runtime system and pose layers for createSkeletonPose (#774)
  • 67js/addons/animation-graph adds root motion (rootMotion option, graph.rootMotion, extractRootMotion; the player controller takes it through a new rootMotion option and state.intentSpeed), interruptible transitions (interrupt: true) and the IK hooks createFootPlacement and createLookAt (#774)
  • 67js/addons/audio adds createAudio, spatial audio for live games on Web Audio: sounds on nodes with a listener on the camera, distance falloff and optional occlusion, triggers from input actions, physics contacts and animation markers, buses with a limiter, and a swappable backend (#775)
  • 67js/addons/streaming evicts lowest asset priority first (new evictionOrder: "distance" opts in to farthest-first within a priority; the default stays least recently used) and adds createWorkerLoader to decode assets in a worker (part of #776)
  • 67js/addons/streaming adds createStreaming, a runtime system that loads assets on demand by distance from a focus point or by game request, with a loader plugin registry (GLB first), a byte budget with least-recently-used eviction, level of detail switching with hysteresis and a loading state game code can read (part of #776)
  • MCP can attach a playable game to a scene (set-game, topic game in scene_capabilities) and scene_export_web writes it as a static web build; 67js/addons/game plays it in a page (#777)
  • 67js/addons/game-rules adds createGameRules, a runtime system that plays a bounded JSON rule document of variables, zone, contact, input, timer and variable triggers and actions, with a deterministic event log (#777)
  • Make the reference netcode relay survive an oversize frame or a socket error instead of crashing, so one bad client cannot drop every player (#778)
  • Add createBroadcastChannelTransport to 67js/addons/netcode, so tabs of one browser play together with no server, with a two-player sample page, a reference WebSocket relay (scripts/netcode-relay.mjs) and a headless Chrome test that plays two pages over both transports with 50 ms injected latency (#778)
  • Cut the netcode sample's lag with 50 ms of injected latency to about 95 ms each way (a snapshot every tick, a 40 ms interpolation delay), keep withInjectedLatency stats live, close the transport on pagehide, and make createBroadcastChannelTransport forget peers silent for peerTimeoutMilliseconds (#778)
  • 67js/addons/netcode adds createNetcode, a runtime system for multiplayer state sync: server or host authority, a transport plugin interface with an in-memory loopback (seeded latency, jitter and loss) and a WebSocket wrapper, bounded delta snapshots with quantisation, client prediction with reconciliation and remote interpolation (first slice of #778)
  • replaceParts(parts, { retainedUnchanged: true }) groups only the parts appended since the last call when a world grows by whole geometries, so loading a large world in slices no longer regroups the whole prefix on every call; a floor that shares a geometry with a world part still groups with it (#779)
  • A renderer built with deferProgramLink: true links its shaders without freezing the page: await renderer.programsReady(); shaderFeatureRepresentatives(parts) picks the few parts that compile the program a whole scene needs, and groupParts builds each material key once; await renderer.prepareFrame(frame) then draws the first frame while the canvas is hidden without blocking on the post, liquid, occlusion and reversed-depth programs a first draw links (#779)
  • loadGltf copies accessors and converts geometry in slices of about 8 ms, and loadHdri decodes a Radiance map a row at a time with a yield between slices, so a large model or sky no longer blocks the page in one piece; the sync readAccessor and parseRadianceHdr stay, and the output is byte for byte the same (#779)
  • The home page opens on the five-world loop (container port, data center, chip fab, warehouse, dark store) instead of the airport ramp: scene tabs, the credits of the playing world, a poster for reduced motion and for the first paint, and the first world downloads after the poster; the airport hero code and its styles are gone, and hero-loop.html stays as the standalone page (#780)
  • Fix the MCP recipe schema refusing the curve fields from #701: scene_create and scene_apply add-geometry now take kind, weights, splines (up to 64), shape, trim and grow on a curve, and points is optional when shape or splines is given (#801)
  • environment.localVolumes with shape: "mesh" and fit: "mesh" now draws in the rasterizer: a mesh pass sums the length of each pixel's ray inside the closed part, exact for any number of shells, and the CPU path tracer and the rasterizer agree within 0.03 on the L-room test (#807)
  • environment.localVolumes takes shape: "mesh" with a part: the part's oriented bounds in the rasterizer and the CPU path tracer, or its closed mesh (fit: "mesh") in the CPU path tracer (#807)
  • Local volumes with noiseAmount now draw the same noise field in the CPU path tracer as in the rasterizer, whose noise moved to a hash that rounds the same on every GPU; the WebGL, WebGPU and progressive tracers still refuse local volumes, with a test each (#808)
  • The bake report names the environment.localVolumes the lightmap bake leaves out (skippedLocalVolumes and a warnings line), and the volumes guide states that a volume emission does not light baked surfaces (#809).
  • Path tracer: a texture with no wrap mode now repeats, as the rasteriser and glTF draw it, so box-projected pattern walls such as the brick room keep their bricks instead of stretching the edge row (#813)
  • Path tracer: lightSelection: "tree" (and auto past 8 lights) now draws only the lights a linked part can see, uniformly among them, instead of walking a tree that counts every light, on the CPU, WebGL2 and WebGPU tracers (#814)
  • A motion track { "kind": "geometry", "id": "sea", "property": "oceanTime" } drives the time of the ocean nodes in a mesh stack, so one video job or timeline renders moving waves (#817)
  • Fix mesh edits such as mesh_stack failing with "render vertices exceed maxVertices across the scene" after a few re-evaluations of a large mesh: the oldest undo revisions are released one at a time before the budget refuses and the answer carries an undoNote with the count, and a scene that is really over the limit is still rejected (#818)
  • The water tag reads the sea's foam as lace (a bright core that breaks into veins, soft and antialiased edges), mirrors the sky with a fresnel term over a deep blue-green body, breaks the sun glint into facets with eight wind ripple bands with warped phases, and an ocean mesh smooths over 180 degrees by default; the physical sky draws its sun as a round disc on a 1024 by 512 map and fades the ground into haze over the first 7 degrees below the horizon, so a finite sea ends in haze, not a tan band (#819)
  • The coplanar search no longer throws "reading 'push'" for a touching pair whose shared box overflows to Infinity (a second route to a NaN priority bin, after non-finite bounds).
  • The coplanar diagnostics no longer throw "Cannot read properties of undefined (reading 'push')" mid-render when parts with non-finite bounds (a NaN vertex, an overflowed pose) share a plane; such parts are left out of the search (#839).
  • NURBS splines take order, knots and knotMode, and meshEdgesToSplines turns mesh edge loops and chains into curve splines (#841)
  • Motion drivers can now set the camera eye, target, fov and roll after the camera tracks, and a constraint influence expression can read custom properties, so one property can dolly the camera or fade a constraint in and out (#842)
  • A jelly softBody takes bounce (0 to 1, default 0): the share of its landing speed the body keeps when it turns around, so a jelly cube rebounds 0.07 m at 0, 0.15 m at 0.5 and 0.47 m at 1 from a 1.95 m drop; softness comes from the existing shape and stiffness (#852)
  • A material graph has an image node that reads a catalog image with the graph's coordinates and mapping (#860)
  • The first path traced draw of a page on the WebGL2 backend builds its kernel faster, because the emitter loop now holds one copy of the shadow ray, with the same pixels (53 of 54 test renders byte-identical, one sun-and-sky scene off by 1/255 in one pixel): lit-interior went from 108 s (one run) to 27 to 47 s over three runs on a loaded RTX 3060, and the old and new kernels compiled side by side took 36 and 7.8 s, 23 and 6.5 s (#861)
  • mesh_edit gains split_edges, delete_loose and flatten; unsubdivide, circularize and extrude_vertices (not representable: a mesh has no loose edge) stay open on #703 (#703).

Install: npm install @codepawlhq/67js@0.19.0