Building a Figma-like Transformation Layer for DeckGL: Harder Than It Looks

⚒️
designengineering
Posted on August 23, 2025

I was working on a feature called Image Alignment for SpatialX, we needed object transformation controls—those familiar resize handles and rotation grips you see in Figma or design tools. Simple, right? Just drag to resize, grab the corner to rotate. How hard could it be? Turns out, super challenging.

Post Content Image
We want to implement this design-tool transform box in our feature

The DeckGL Constraint

Since we're already using DeckGL for our spatial visualization, switching to something like Fabric.js wasn't an option. Plus, Fabric's interaction patterns felt clunky compared to the smooth, intuitive feel of modern design tools. I wanted that Figma-like experience where everything just feels right.

The Invisible Complexity

Here's the thing about good UX—when it works perfectly, you don't notice it exists. You just drag a corner and expect the object to resize while keeping the opposite corner fixed. You rotate something and expect the cursor to show the right direction. These interactions are so natural we take them for granted.

But implementing them? That's where things get messy.

We have to keep the top fixed when dragging from the bottom.

Problem 1: The Math Gets Wild

Resizing a rotated object isn't just about changing width and height. When you drag the top-right corner of a 45-degree rotated rectangle, which direction should it resize? The math involves coordinate transformations, rotation matrices, and ensuring the opposite edge stays perfectly fixed.

I found myself deep in trigonometry, calculating scale origins and transforming mouse coordinates between different coordinate spaces. There were moments where I stared at the screen wondering if I'd bitten off more than I could chew.

Problem 2: No Standard Rotation Cursors

This one caught me off guard. CSS (and operating systems) provide built-in cursors for resizing—nw-resize, ne-resize, etc. But there are no standard cursors for rotation handles.

Every design tool handles this differently. Figma, Sketch, Penpot, Framer—they all had to create custom cursors for rotation. Some use the same rotation icon for all corners, others create directional rotation cursors that show which way the corner will move when dragged.

I ended up building custom SVG rotation cursors that change based on which corner you're hovering over. It's one of those details where every design tool has to reinvent the wheel because there's no standard solution.

Post Content Image
These rotating cursors are customized to map with native cursors (yes, there are 4 of them for 4 corners with different colors for macOS and Windows).

Learning from the Best

When I got stuck (which was often), I looked at how the pros do it. I dove into tldraw's source code to understand their rotation-aware resize logic. I studied Penpot's geometric transformations. Each reference helped piece together the puzzle.

The breakthrough came from tldraw's approach: calculate the distance from the drag point to the scale origin, rotate both points by the negative object rotation, then compute the scale ratio. Elegant once you see it, but took forever to figure out.

The Two-Zone Solution

I ended up with a two-zone interaction model:

  • Resize zones: Thin areas along edges and corners for resizing
  • Rotate zones: Outer zones around corners for rotation

This separation makes interactions predictable. You know exactly what will happen when you hover over different areas. It's inspired by Figma's interaction model but adapted for DeckGL's layer-based architecture.

Post Content Image
Showing resize and rotate zones for easier debug

The Magic of AI Pair Programming

I have to be honest, Claude Code was incredible throughout this process. When I got lost in coordinate transformations or couldn't figure out why the rotation center was off, Claude Code helped break down the math and spot the bugs. It's like having a patient math tutor who never judges you for making the same trigonometry mistake three times.

The Result

What started as a "quick feature" became a full transformation library. It handles move, resize, and rotate operations with pixel-perfect precision. The cursors change based on rotation. The resize behavior feels natural even on rotated objects. The UI is clean and modern.

But most importantly, it just works. Users can transform objects without thinking about the complexity underneath—which was the whole point.

Demo prototype

Takeaways

  1. Simple UX often hides complex implementation. What looks trivial from the user's perspective can require deep technical work.
  2. Study the masters. Open source projects like tldraw and Penpot are goldmines for learning interaction patterns.
  3. AI pair programming is powerful. Having Claude as a coding partner made tackling the complex math much more manageable.
  4. Constraints breed creativity. Being locked into DeckGL forced me to build something custom that ended up being better than off-the-shelf solutions.