Skip to content

Transform 2D

Moves, scales, and rotates its input in 2D space. Works on images (GPU sampling), shapes, and points (CPU affine).

Category: Utility Menu path: Utility > Transform2D

Ports

PortTypeDirectionDescription
inimageRgba16f / shape / pointsinputInput to transform. Output type matches input.
outimageRgba16f / shape / pointsoutputTransformed result

Parameters

ParamTypeDefaultDescription
positionModeenumPixelsHow position is interpreted. See Position mode below.
positionAnchorenumCenterComp edge/corner the offset is measured from when Position Mode is Pinned. Labeled Pin To in the UI. Hidden in other modes.
positionvec2(0, 0)Translation. Units depend on Position Mode: pixels-from-comp-center (Pixels), normalized 0–1 from top-left (Normalized), or pixel offset from the chosen Pin To edge (Pinned).
scalevec2(1, 1)Scale factor (1 = 100%). Linked by default.
rotationscalar0Rotation in degrees
anchorvec2(0.5, 0.5)Pivot point in normalized coordinates. (0,0) = top-left, (0.5,0.5) = center, (1,1) = bottom-right. The frame of reference depends on anchorSpace.
anchorSpaceenumContentWhat the anchor is normalized to. Content = the input's bounding box (recommended, composes correctly in chains). Comp = the composition. Texture inputs ignore this — anchor is always canvas-normalized for textures.

Expose Channels

PortTypeOverrides
position_invec2position
scale_invec2scale
rotation_inscalarrotation
anchor_invec2anchor

How It Works

For images: applies an inverse affine matrix via GPU sampling. The engine evaluates at a larger canvas size (overscan) to prevent content from being clipped at composition boundaries during transforms.

For shapes and points: applies a CPU affine transform to vertex positions (~1us). This is the standard way to position geometry before rasterization via DrawShape/DrawPoints.

Transform2D is the sole spatial transform node. The Output node handles compositing (opacity + blend mode) but has no spatial transform — use T2D for all positioning.

Position mode

positionMode decides how position translates into a final pixel offset. The downstream pipeline (overscan computation, matrix construction, gizmo) only ever sees absolute pixels-from-comp-center; the mode is resolved at the start of T2D evaluation.

All three modes share one forward formula: T(P) = (P − pivot) × S × R + position. position is where the anchor lands in comp space. The mode just controls how the position value's coordinates are interpreted before being resolved to comp-space pixels:

  • Pixels (default): position is in absolute pixels with comp center = (0, 0). (0, 0) = anchor at comp center, (960, 0) on a 1920px-wide comp = anchor at right edge.
  • Normalized: position is [0, 1] from top-left to bottom-right. (0.5, 0.5) = anchor at comp center, (0.5, 0.78) = anchor at a typical lower-thirds Y. Designs survive resolution and aspect changes — (0.5, 0.78) lands at the same relative spot whether the comp is 1920×1080 or 1080×1920.
  • Pinned: the Pin To picker (positionAnchor) selects a comp corner or edge (TopLeft, BottomCenter, etc.) and position is a pixel offset from that edge in standard screen coords (+X right, +Y down). For "32px from the bottom-right corner with the content's bottom-right corner pinned there", set Pin To = BottomRight, anchor=(1, 1), and position=(-32, -32). (Distinct from the Anchor Point picker, which sets the rotation/scale pivot in content space.)

Because position is where the anchor lands, you get clean placement with zero half-width math: anchor=(0, 0.5) plus any position places the content's left-middle at that position.

Switch modes freely while iterating — values aren't auto-converted between modes, so the visual position will jump when you change positionMode. Re-key once you're settled on a mode.

Anchor space

anchorSpace controls where the pivot point lives:

  • Content (default): Anchor is normalized within the input's AABB. Anchor (0.5, 0.5) pivots around whatever the input's current bounding box center is. This composes correctly when chaining T2Ds — a second T2D added after the first scales/rotates around the current shape, not around comp center.
  • Comp: Anchor is normalized within the composition. Anchor (0.5, 0.5) pivots around comp center regardless of where the input sits. Useful for spinning or scaling content around a fixed point in comp space (e.g. rotate a whole scene around its top-left corner with anchor (0, 0)).

Rule of thumb: leave it on Content for 99% of cases. Flip to Comp when you specifically want a pivot anchored to the composition rather than the content.

Texture inputs resolve to Comp behavior regardless of the param value — the GPU texture render path doesn't branch on anchorSpace, it always pivots at anchor * compSize. The engine reports this as effectiveAnchorSpace: "Comp" in ResolvedNodeTransform so the gizmo math stays in sync.

Anchor Point picker & snapping

Two ways to place the anchor (rotation/scale pivot) precisely:

  • Anchor Point picker — a 9-point grid in the Properties panel (under the Anchor row). Click a cell to snap the anchor to that corner / edge / centre of the content (Top-Left → (0, 0), Centre → (0.5, 0.5), Bottom-Right → (1, 1)). The picker keeps the content visually in placeposition auto-compensates — so picking a preset moves the pivot, not the shape.
  • Anchor handle snapping — dragging the anchor crosshair in the viewport snaps to those same 9 content points within a few pixels; the crosshair turns yellow while latched. Hold Cmd/Ctrl to drag freely past a snap. (Honors the viewport snap toggle.)

Both move only the pivot — the content stays put, exactly like dragging the handle freely.

Positioning T2D relative to Draw nodes

Prefer to do all transforms before Draw nodes. The pattern Shape → T2D → T2D → … → DrawShape → Output composes cleanly: each T2D pivots around the current geometry AABB, the gizmo wraps the shape tightly, and chained T2Ds inherit each other's positioning the way nested parent transforms do.

T2D placed after a Draw node (DrawShape / DrawText / DrawPoints) operates on a canvas-sized texture. That's by design — rasterized output is sized for compositing uniformity. But it means the gizmo bounds wrap the full canvas rather than the visible drawn content, because the engine reports inputBounds as the texture's pixel dimensions. The transform itself works correctly; only the gizmo sizing is loose. Use this pattern when you specifically want to transform the rasterized canvas as a whole (canvas-level effects, compositing multiple drawn layers), not for positioning individual shapes.

(Tracked as a future engine improvement: have rasterizer nodes report their tight content AABB alongside the texture so downstream gizmos can wrap actual pixels.)

Usage Examples

Basic: Position a layer

Every image layer typically has an ImageSource -> Transform2D -> Output chain. T2D controls where the image appears in the composition.

Geometry pipeline

Rectangle -> Transform2D -> DrawShape -> Output. The T2D positions and scales the shape before it's rasterized.

Procedural animation

Connect Time -> Math (multiply by speed) -> T2D's rotation_in expose channel for continuous rotation. Or use Noise.scalar for organic drift on position.

Tips

  • Scale is linked by default (X and Y move together). Click the link icon in Properties to unlink for independent axis scaling.
  • Shelf tools (R, O, P, G, T) auto-create a geometry -> T2D -> DrawShape -> Output chain
  • Click a layer in the viewport to auto-select its T2D node. If no T2D exists, one is auto-inserted.
  • Shift constrains gizmo rotation to 15-degree increments
  • Shift constrains gizmo scale to proportional
  • Output — compositing interface (opacity + blend mode, no spatial transform)
  • DrawShape — rasterizes shapes positioned by T2D