project-darkwood

Saturday, February 21, 2026
Cedric
Cedric 3:09 AM
Now update the maskBlend node to use auto-detection:
Cedric
Cedric 3:09 AM
Deploying now. Dropped the dropdown — Mask Blend now auto-detects:

Only A + mask: A visible where mask is white, transparent where black
Only B + mask: B visible where mask is black, transparent where white
A + B + mask: blends between them like before
Also added a generic ${connected:inputName} template any node can use going forward.
Cedric
Cedric 3:09 AM
Building and deploying. Should be live in about a minute.
Marian
Marian 3:10 AM
Good. can we put some background for the transparency to show through? Like the iconic photoshop checkers?
Cedric
Cedric 3:11 AM
The simplest approach: add the checkerboard as a CSS background on the canvas element. The canvas has transparency support already (alpha channel is preserved in readTextureToCanvas), so a CSS checkerboard behind it will show through automatically.
Cedric
Cedric 3:11 AM
Done — Photoshop-style dark checkerboard behind all node previews. Transparency shows through naturally since the canvas alpha channel is already preserved. Deploying now.
Marian
Marian 10:03 AM
SDF arrow sucks. fix it. add more params
image.png image.png
Cedric
Cedric 10:05 AM
Yeah, this is a crude box+triangle combo. Let me rewrite it with proper params — head length, shaft length, rounding, rotation angle, and a better SDF construction:
Cedric
Cedric 10:05 AM
Wait, the glslHelpers has ${helpers} without the backslash escape. Let me fix:
Cedric
Cedric 10:05 AM
Rewrote the arrow from scratch. New params:

Length — overall arrow size
Shaft Width — thickness of the stem
Head Length — how much of the arrow is the arrowhead (proportion, 0.05–0.8)
Head Width — how wide the arrowhead spreads
Rounding — rounds all corners
Tail — Flat (default), Notched (V-cut at bottom), or Pointed (tapers to a point)
Uses a proper general triangle SDF now instead of the isosceles hack. Deploying.
Marian
Marian 10:50 AM
Double check that base parameters are correctly present only on nodes where it makes sense and that it is wired up correctly
Cedric
Cedric 10:52 AM
Good. So isGeneratorNode checks: not in EXCLUDED_TYPES, and has no non-mod texture inputs. Let me verify which nodes get classified as generators vs operators:
Cedric
Cedric 10:52 AM
Now let me check for issues:

1. SDF shape nodes (generators) — These get universal params. Makes sense: you'd want to rotate/scale/offset an SDF circle.
2. SDF operator nodessdfAnnular, sdfIntersect, sdfOffset, sdfOnion, sdfOutline, sdfRepeat, sdfRound, sdfSmoothIntersect/Subtract/Union, sdfSubtract, sdfUnion — These are operators (have non-mod inputs). They do NOT get universal params. Correct.
3. colorConstant — Generator (no inputs). Gets universal params... but rotating/scaling a constant color is meaningless. Should be excluded.
4. imageInput — Generator. Gets universal params. Makes sense (offset/scale an image).
5. sdfText, sdfIcon — Generators with customRender. They already handle universal params via the post-process transform in evaluator. Need to verify the compiler handles them too... but customRender nodes are always bitmap (no glslFunction), so they're never in composed segments. Fine.
Let me check a few more edge cases:
Cedric
Cedric 10:52 AM
Good — sdfText and sdfIcon are forced to texture mode (customRender). Universal params work via the post-process transform shader in the evaluator.

Now let me check that the SDF shape nodes' glslFunction properly uses the transformed UV (universal params). The compiler wraps generators with UV transform code. Let me verify with sdfCircle:
Cedric
Cedric 10:52 AM
The glslFunction uses uv — and in the compiler, generator nodes get their uv replaced with _tuv (transformed UV). That's correct.

Let me also verify that seed is wired up correctly for nodes that have it:
Cedric
Cedric 10:52 AM
These are all generators. The seed param is additive: node's own seed + uBase_seed. Correct.

The only fix needed: exclude colorConstant from universal params.
Cedric
Cedric 10:52 AM
Checked everything. Here's the audit:

Generators (67 nodes) — get universal params (rotation, offset, scale, amplitude, seed):
All correctly classified. These have no non-mod texture inputs, so transform/scale/rotate makes sense.

Operators (62 nodes) — NO universal params:
All correctly classified. These take texture inputs and process them — no spatial transform needed at this level.

One fix applied: colorConstant was incorrectly getting universal params (rotating a solid color is pointless). Now excluded alongside constant and output.

SDF nodes verified:

• Shape generators (circle, box, star, etc.) → universal params :whitecheckmark:
• SDF operators (union, subtract, intersect, etc.) → no universal params :whitecheckmark:
sdfText/sdfIconcustomRender + pipelineHint: 'texture', universal params via post-process transform :whitecheckmark:
Seed wiring: 15 noise-based nodes have own seed param, additively combined with uBase_seed. Correct.
Marian
Marian 10:56 AM
are you sure no operators are sensitive to position/rotation, etc?
Cedric
Cedric 10:56 AM
Good catch. Let me think about this...
Cedric
Cedric 10:56 AM
You're right. These operators do UV manipulation — twirl, kaleidoscope, spherize, pinchBulge, lensDistortion all work relative to a center point. Being able to offset/rotate their effect center would be useful. Same for transform2d — which is literally a spatial transform operator.

But the current universal params system transforms the input UV before the node runs, which makes sense for generators (they generate from UV space). For operators, it would mean transforming the source texture sampling, which is different — it would shift/rotate the input rather than the effect itself.

Actually, that's still useful. transform2d is literally that — it already has its own offset/rotation/scale params. But for something like twirl, universal params would let you shift the whole twirl effect.

However — adding universal params to ALL operators would clutter the UI and add unnecessary overhead. The real question is: which operators would benefit?

Operators that are position/rotation sensitive:

twirl — has centerX/centerY already
kaleidoscope — center-dependent
spherize, pinchBulge — center-dependent
polarCoordinates — center-dependent
radialBlur — center-dependent
directionalBlur — angle-dependent
directionalWarp — angle-dependent
These already have their own center/angle params though. Universal params would be redundant.

The ones that don't have built-in position control and could benefit:

pixelate — grid alignment is position-sensitive
tile — tiling origin matters
halftone — wait, that's already a generator
I think the current split is actually correct. The spatial operators already have their own position controls built into their specific params. Adding universal params on top would be confusing (two layers of transform).

What do you think — is there a specific operator where you're missing position/rotation control?
Marian
Marian 11:16 AM
Good, fix all
Cedric
Cedric 11:18 AM
Let me check exactly which operators are missing position/rotation controls:
Cedric
Cedric 11:18 AM
Missing center controls:

kaleidoscope — no center, no rotation offset
spherize — no center
pinchBulge — no center (has radius)
polarCoordinates — no center
Already have them:

radialBlur — has centerX/Y ✓
directionalBlur — has angle ✓
directionalWarp — has angle ✓
twirl — has centerX/Y ✓
Let me also check pixelate and tile — these could use offset for grid alignment:
Cedric
Cedric 11:18 AM
Now let me add centerX/centerY and rotation to the ones that need it. I'll also add offsetX/Y to pixelate and tile.
Cedric
Cedric 11:18 AM
Now pixelate (add offset) and tile (add offset):
Cedric
Cedric 11:18 AM
Done. Here's what got added:


| Node | Added params |
| ----------------- | --------------------------------------------------- |
| Kaleidoscope | Center X/Y, Rotation |
| Spherize | Center X/Y, Radius |
| Pinch/Bulge | Center X/Y |
| Polar Coordinates | Center X/Y, Rotation |
| Pixelate | Offset X/Y |
| Tile | Offset X/Y, Mirror mode (repeat vs mirrored tiling) |

Operators that already had proper controls were left alone: radialBlur (centerX/Y), directionalBlur (angle), directionalWarp (angle), twirl (centerX/Y).
Marian
Marian 2:00 PM
good, make sure basic params are moddable by params as well
Cedric
Cedric 2:02 PM
Let me check how mod inputs work on operators currently:
Cedric
Cedric 2:02 PM
None of them have mod inputs yet. Let me check how an existing operator with mod inputs works — like twirl:
Cedric
Cedric 2:02 PM
All generators. No operators have mod inputs yet. The mod system works by adding mod<paramName> inputs and umodAmount_<paramName> uniforms. The shader reads the mod texture and applies it. Let me add mod inputs for the spatial params on all 6 operators.
Cedric
Cedric 2:02 PM
Done. All 6 operators now have mod inputs on their spatial params:

Kaleidoscope: center X/Y mod, rotation mod
Spherize: strength mod, center X/Y mod
Pinch/Bulge: strength mod, center X/Y mod
Polar Coordinates: center X/Y mod, rotation mod
Pixelate: scale mod, offset X/Y mod
Tile: tiles X/Y mod, offset X/Y mod
Connect any grayscale texture to the mod ports and use the mod amount slider to control intensity. Deploying now.
Marian
Marian 2:43 PM
arrow still looks very very shitty
Cedric
Cedric 2:45 PM
Let me look at it myself.
Cedric
Cedric 2:45 PM
Let me add an SDF Arrow node to see it:
Cedric
Cedric 2:45 PM
Let me just look at the actual arrow code. The issue is probably the geometry — let me rewrite with a cleaner approach using Inigo Quilez's exact SDF primitives:
Cedric
Cedric 2:45 PM
I see the problem — the defaults produce a weirdly proportioned arrow. headLength: 0.35 means 35% of the total length is head, with the rest being a thin shaft (shaftWidth: 0.025). It looks like a stick with a small triangle on top, not a proper arrow.

Let me completely rewrite this with better defaults and a cleaner construction — a proper vector arrow like you'd see in design tools:
Cedric
Cedric 2:45 PM
Completely rebuilt the arrow. Key changes:

Better defaults: shaft width 0.04 (was 0.025), head length 0.12 as absolute value (was 0.35 as proportion of total — confusing), head width 0.1. Should look like a proper arrow out of the box now.

4 styles:

Chevron (default) — classic arrow with rectangular shaft + triangular head
Filled — same but fills the gap between shaft and head wings
Thin — stroke-only: line shaft with chevron tip (uses sdSegment for clean strokes)
Double — two stacked chevron heads with a shaft
Head length is now absolute, not a proportion. Much more intuitive to control.

Deploying now.
Marian
Marian 4:06 PM
hate it. delete
Cedric
Cedric 4:07 PM
Gone. Deploying.
Marian
Marian 4:12 PM
Blur and other operators have almost no inputs. Like why can i not modulate blur radius by some input?
Cedric
Cedric 4:13 PM
Fair point. Let me audit all operators and add mod inputs where they make sense.
Cedric
Cedric 4:13 PM
Big list. Let me prioritize — the most impactful ones to modulate are the ones where spatial variation creates interesting effects. I'll spawn a sub-agent for this bulk work:
Cedric
Cedric 4:13 PM
Spawned a sub-agent to add mod inputs to 26 operator nodes. This covers:

Filters: blur, boxBlur, sharpen, edgeDetect, emboss, directionalBlur
Color: hslAdjust, levels, posterize, threshold
Distortion: warp, twirl, lensDistortion, directionalWarp, radialBlur
Compositing: blend, normalMap
Transform: transform2d
SDF ops: smoothUnion/Subtract/Intersect, outline, annular, offset, repeat, toMask
Every float param on these nodes will get a grayscale mod input. Should be done in ~5 min, then auto-deploys.
Cedric
Cedric 4:19 PM
All 26 operators now have mod inputs on their float params — blur radius, warp strength, HSL adjustments, SDF smoothness, everything. Live on darkwood.sandscape.cc.
Marian
Marian 4:45 PM
Go through this channels message including threads. Collect everything important and store in you memories
Cedric
Cedric 4:47 PM
Let me continue where I left off — reading more channel messages and threads to capture everything important.
Cedric
Cedric 4:47 PM
No more messages before that — the channel history starts from around Feb 20-21. Let me check for any thread messages I might have missed.
Cedric
Cedric 4:47 PM
I've now read through the entire channel history. The summary already captured the key points well. Let me update the memory files with everything important.
Cedric
Cedric 4:47 PM
Now let me push these memory updates.
Cedric
Cedric 4:47 PM
Done. I've gone through the entire #project-darkwood Slack channel (about 80+ messages from Feb 21) and stored everything important in memory/2026-02-21.md. Here's what I captured:

Key topics:

• Full timeline of SDF system implementation (auto-conversion, pipeline toggle, dual mask output)
• WebGL state corruption bug (getSdfAsMask clobbering GL state) and fix
• Mask Blend auto-detection enhancement (no dropdown, auto-detect connected inputs)
• Checkerboard transparency background
• Universal params audit (colorConstant exclusion)
• Operator position params added to 6 nodes
• Mod inputs added to all 26 operators
• SDF Arrow saga (3 rewrites → deleted)
• Deploy lesson (always deploy after push)
• Marian's feedback patterns (prefers auto-detection, quick to reject poor quality, expects immediate deploys)