paperlab@0.2.0
Minor Changes
-
3b38a22: New:
crumple— paper that has been handled.Seven deformers now, and the new one is the first that crushes a sheet.
waveandfoldwere the nearest and neither reads as crumpled, which made this the biggest single gap in the set: a crumple is the most recognisable paper state there is.It ships as the whole slice — the
crumpledeformer (JSdisplaceplus its GLSL twin, held together by three new cases inpnpm test:parity), acrumplebehavior (progress,coarseness,ball,seed), and acrumpled-notepreset.The field is the gap between the two nearest points of a jittered cell grid, signed per cell. It vanishes on every cell boundary, so the sheet stays continuous, and its gradient flips across one — which is a crease. What you get is an irregular polygonal network of facets alternating toward and away from you, rather than the periodic egg-crate or the smooth hammered-metal look the two earlier attempts produced. The normals are the point: a crumple that does not shade its own facets is a noisy sheet, not a crushed one.
It is the most expensive deformer in the set, and measurably so:
pnpm perf:fieldputs a field of them about 45% longer per frame than the same field of an undeformed preset. Almost none of that is geometry —segments: 'auto'already gives every sheet 72 a side, so itsminSegments: 72is a floor that only bites when a preset asks for a coarser grid by hand. The cost is the nine cell lookups per probe, three probes deep for the vertex normal.Also:
describeConfignow has a phrase forcrumple, and a test asserts that every registered behavior has one, so a new behavior can no longer describe itself as nothing. -
09416c5:
drape,crumpleand thecrumplebehavior are now exported like every other deformer and behavior.roll,curl,bend,foldandwavewere each exported individually — their deformer object, options schema and options type — whiledrapeandcrumplewere reachable only throughgetDeformer(id). Nothing depended on the difference, which is exactly why it was worth closing: an API with an arbitrary hole in it is a papercut for the first person who trips over it, and the reference site now documents all seven.This is deliberately the reversible direction. The alternative was removing all seven, which is a breaking change and belongs to the pre-1.0 export trim rather than to a tidy-up. When that trim happens, the deformer objects and their schemas should go as one group of seven.
-
b45980b: Fix: the props now accept what the docs say they accept, and
surfaceis finally one of them.Two bugs, same root.
<Paper surface={{ grain: 0.3 }} />was documented in the README,AGENTS.mdanddocs/llms.txtand was not a prop at all — it failed to typecheck, and in plain JSresolveConfigdropped it on the floor, so the effect you asked for silently never happened. Andcontent,behavior,deformersandphysicstook each schema's parsed type rather than its input type, which demanded every field of every nested object: the README's own example —content={{ type: 'receipt', store: 'acme.dev', items: [...] }}— did not compile.Both are fixed.
surfaceandsceneare real props now (surface merges over the stock's defaults rather than replacing them, sosurface={{ grain: 0.6 }}on thermal keeps thermal's banding), and every config prop takes the schema's input type, so anything with a default stays optional. The schema now exports both types for each config —ContentConfigInput,BehaviorConfigInput,SurfaceConfigInput,PhysicsConfigInput,DeformerInstanceConfigInput,SceneConfigInput— andconfig/props.test.tspins the documented examples at both the type level and at runtime, so a prop cannot quietly go back to an inferred type.No runtime behaviour changes for code that already compiled, except that a
surfaceprop now actually applies.
Patch Changes
-
1141986: Fix:
bendand its GLSL twin disagreed at low curvature, and the parity gate never looked there.The arc's in-plane shift is
r·sin θ − d, anddisr·θ— so for a gentle bend it is a difference of two nearly-equal large numbers, and the answer is whatever bits survive.r(1 − cos θ)has the same problem. JS computes both in float64 and gets away with it; the GLSL twin computes them in float32 and does not. The two paths were 6.1e-4 apart atcurvature: 0.35— past the parity gate's 5e-4 epsilon — meaning hero mode and field mode were rendering measurably different arcs.It went unnoticed because the gate only ever exercised
|curvature| ≥ 0.6, whilephoto-print— the field starter preset, and the one every gallery layout is demoed with — bends at0.35, squarely inside the untested band.bendis now written in its cancellation-free form on both sides:r(1 − cos θ)as2r·sin²(θ/2), and the in-plane shift through asin(x) − xhelper that uses a series below |x| = 1 and the direct form above it. Same arc to sixteen places — only the float32 half could tell the difference, and that is exactly the half that was wrong. Worst-case parity error at 0.35 drops from 6.1e-4 to 2.1e-5, and the existing bend cases improved by an order of magnitude too. Two permanent low-curvature parity cases now cover the band, including the gentlest arc the schema allows. -
963861b: Docs: document the community loop. A
.paperfile someone shares with you is already a preset object —<Paper preset={theirPaper} />orregisterPreset(name, theirPaper)— so it goes straight into a project without being expanded into individual props. The README,AGENTS.md, anddocs/llms.txtnow say this explicitly, andconfig/shared-paper.test.tspins the round-trip so the promise cannot silently break.CONTRIBUTING.mdnow leads with the fact that sharing a paper needs no fork and no PR; the contribution ladder is for work you want shipped inside the library.