The Forgotten Primitive

Most graphic libraries support linear, radial, conical (angular) gradients. Same is true for SVG, CSS. But figma has a unique diamond gradient. Think Manhattan distance instead of Euclidean.

The Problem

A radial gradient uses distance = sqrt(dx² + dy²). Colors spread in circles.

A diamond gradient uses distance = max(|dx|, |dy|). Colors spread in squares.

There is a trick of overlaying two linear gradients that could suite for CSS in some cases, but not in all, so we can't fake a generic diamond gradient implementation.

How diamond gradient is actually looking?

I would show a simple pixelart implementation of diamond. In this example you could drag center\corner points to see how gradient behaves

Understanding Different Distance Metrics

The shape of a gradient comes from how you measure "distance from center". Let's compare three ways:

Euclidean distance (what radial gradients use):

distance = sqrt(x² + y²)

This is the straight-line distance you measure with a ruler. Points equidistant from the center form a circle.

Example: Point (3, 4) from origin:

distance = sqrt(3² + 4²) = sqrt(9 + 16) = sqrt(25) = 5

Manhattan distance (L₁ norm, taxi-cab distance):

distance = |x| + |y|

Named after walking in Manhattan—you can't cut diagonally through buildings, so you walk along streets (x) then avenues (y). Points equidistant form a diamond rotated 45°.

Example: Point (3, 4) from origin:

distance = |3| + |4| = 7

Chebyshev distance (L∞ norm, what we use):

distance = max(|x|, |y|)

The "king's move" in chess—a king can move one square in any direction (including diagonal), so what matters is the larger of the two coordinates. Points equidistant form an axis-aligned square.

Example: Point (3, 4) from origin:

distance = max(|3|, |4|) = 4

All points at distance 4: (4,0), (4,4), (0,4), (-4,4), (-4,0), (-4,-4), (0,-4), (4,-4), and everything in between on the square's edges.

Why "Diamond" for a Square Gradient?

In Figma's terminology, it's called "diamond" because visually the gradient spreads in a box/diamond shape. Mathematically it's the L∞ norm producing axis-aligned squares, but "diamond gradient" is the design tool name.

For a point at (3, 5): max(3, 5) = 5. Same distance as (5, 5), (5, 3), (5, 0), or (5, -5). All these points are equidistant—they form a square of side length 10 (from -5 to +5 in both directions).

Skia

Skia doesn't have these. Let's add it. The Solution — new Gradient Type

Let's Implement SkGradientShader::MakeDiamond() following Skia's gradient API pattern.

Distance function (src/shaders/gradients/SkDiamondGradient.cpp):

float distanceFromCenter(float2 pos) {
  return max(abs(pos.x), abs(pos.y));  // Chebyshev distance
}

Shader implementation:

sk_sp SkGradientShader::MakeDiamond(
    const SkPoint& center,
    SkScalar radius,
    const SkColor4f colors[],
    sk_sp colorSpace,
    const SkScalar pos[],
    int colorCount,
    SkTileMode mode,
    uint32_t flags,
    const SkMatrix* localMatrix) {

  // Similar structure to MakeRadial
  return sk_make_sp(center, radius, ...);
}

The actual rendering happens in a fragment shader that samples the distance, looks up the color in the gradient, and returns the result.

The SkColor Overload Issue

Added MakeDiamond() with SkColor4f parameter (modern API). Tried to use it:

SkColor4f colors[] = {{1, 0, 0, 1}, {0, 0, 1, 1}};
auto shader = SkGradientShader::MakeDiamond(center, radius, colors, ...);

Linker error: undefined reference to MakeDiamond(SkColor).

Turns out Skia has two parallel gradient APIs: one for SkColor (legacy, 32-bit RGBA), one for SkColor4f (modern, floating-point). We implemented the 4f version, but CanvasKit's JavaScript API still used the SkColor path internally.

Had to add the overload:

sk_sp SkGradientShader::MakeDiamond(
    const SkPoint& center,
    SkScalar radius,
    const SkColor colors[],  // Not SkColor4f
    const SkScalar pos[],
    int count,
    SkTileMode mode,
    uint32_t flags,
    const SkMatrix* localMatrix) {

  // Convert SkColor to SkColor4f and call the main implementation
  SkSTArray<4, SkColor4f> colors4f(count);
  for (int i = 0; i < count; i++) {
    colors4f.push_back(SkColor4f::FromColor(colors[i]));
  }
  return MakeDiamond(center, radius, colors4f.data(), nullptr, ...);
}

Now both APIs work. The SkColor version converts to SkColor4f and delegates.

Distance Field Properties

The diamond distance function max(|x|, |y|) is the L∞ norm (supremum norm). It's the limiting case of (|x|^p + |y|^p)^(1/p) as p → ∞.

Properties:

For gradients, the non-differentiability doesn't matter—we're sampling discrete color stops, not requiring smooth derivatives.

Results

Diamond gradients now work in CanvasKit. The implementation is ~200 lines, following Skia's existing gradient structure:

The math is simpler than radial gradients (no sqrt), so it's actually slightly faster.

Sometimes the "missing" feature is just a different distance function. Skia had all the infrastructure—gradient shaders, color interpolation, tile modes. We just needed one new distance calculation.