Skip to main content
Interpolation functions map input values to output values, enabling smooth transitions and animations. React Native Skia provides utilities for interpolating numbers, colors, vectors, and paths.

interpolate

Maps a value from an input range to an output range.
import { interpolate } from "@shopify/react-native-skia";

// Basic interpolation
const value = interpolate(50, [0, 100], [0, 1]);
console.log(value); // 0.5

// Non-linear mapping
const scaled = interpolate(50, [0, 100], [0, 200]);
console.log(scaled); // 100

Function Signature

function interpolate(
  value: number,
  inputRange: number[],
  outputRange: number[],
  options?: ExtrapolationType
): number

Parameters

value
number
required
The input value to interpolate
inputRange
number[]
required
Array of input values (must be monotonically increasing)
outputRange
number[]
required
Array of output values (same length as inputRange)
options
ExtrapolationType
Extrapolation behavior:
  • "extend": Continues the pattern beyond the range (default)
  • "clamp": Clamps to the nearest edge value
  • "identity": Returns the input value unchanged
  • { extrapolateLeft: Extrapolate, extrapolateRight: Extrapolate }

Examples

Linear Interpolation

// Map progress (0-1) to position (0-300)
const progress = 0.5;
const position = interpolate(progress, [0, 1], [0, 300]);
// position = 150

Multi-Point Interpolation

// Ease in-out effect
const progress = 0.5;
const eased = interpolate(
  progress,
  [0, 0.25, 0.75, 1],
  [0, 0.1, 0.9, 1]
);

With Extrapolation

import { Extrapolate } from "@shopify/react-native-skia";

// Clamp to range
const value = interpolate(150, [0, 100], [0, 1], "clamp");
// value = 1 (clamped)

// Extend beyond range
const extended = interpolate(150, [0, 100], [0, 1], "extend");
// extended = 1.5 (extended)

// Different extrapolation on each side
const mixed = interpolate(150, [0, 100], [0, 1], {
  extrapolateLeft: Extrapolate.CLAMP,
  extrapolateRight: Extrapolate.EXTEND,
});

interpolateColors

Interpolates between colors.
import { interpolateColors } from "@shopify/react-native-skia";

const progress = 0.5;
const color = interpolateColors(
  progress,
  [0, 1],
  ["red", "blue"]
);
// Returns an RGBA color array

Function Signature

function interpolateColors(
  value: number,
  inputRange: number[],
  outputRange: Color[]
): SkColor

Examples

Gradient Effect

import { useSharedValue, useDerivedValue } from "react-native-reanimated";
import { Canvas, Circle, interpolateColors } from "@shopify/react-native-skia";

export default function ColorTransition() {
  const progress = useSharedValue(0);
  
  useEffect(() => {
    progress.value = withRepeat(
      withTiming(1, { duration: 2000 }),
      -1,
      true
    );
  }, []);
  
  const color = useDerivedValue(() => {
    return interpolateColors(
      progress.value,
      [0, 0.5, 1],
      ["red", "yellow", "green"]
    );
  });
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Circle cx={150} cy={150} r={100} color={color} />
    </Canvas>
  );
}

mixColors

Mixes two colors by a factor.
import { mixColors } from "@shopify/react-native-skia";

const mixed = mixColors(0.5, "red", "blue");
// Returns a color halfway between red and blue

Function Signature

function mixColors(
  value: number,
  color1: Color,
  color2: Color
): SkColor

interpolatePaths

Interpolates between path shapes.
import { interpolatePaths, Skia } from "@shopify/react-native-skia";

const path1 = Skia.Path.MakeFromSVGString("M 0 0 L 100 0 L 50 100 Z");
const path2 = Skia.Path.MakeFromSVGString("M 0 100 L 100 100 L 50 0 Z");

const morphed = interpolatePaths(0.5, [0, 1], [path1, path2]);

Function Signature

function interpolatePaths(
  value: number,
  inputRange: number[],
  outputRange: SkPath[],
  options?: ExtrapolationType,
  output?: SkPath
): SkPath

Requirements

  • Paths must be interpolatable (same number of points and commands)
  • Use path1.isInterpolatable(path2) to check compatibility

Examples

Morphing Shapes

import { useSharedValue, useDerivedValue, withRepeat, withTiming } from "react-native-reanimated";
import { Canvas, Path, interpolatePaths, Skia } from "@shopify/react-native-skia";
import { useEffect } from "react";

const triangle = Skia.Path.MakeFromSVGString("M 50 0 L 100 100 L 0 100 Z");
const square = Skia.Path.MakeFromSVGString("M 0 0 L 100 0 L 100 100 L 0 100 Z");

export default function MorphingShape() {
  const progress = useSharedValue(0);
  
  useEffect(() => {
    progress.value = withRepeat(
      withTiming(1, { duration: 2000 }),
      -1,
      true
    );
  }, []);
  
  const path = useDerivedValue(() => {
    return interpolatePaths(
      progress.value,
      [0, 1],
      [triangle, square]
    );
  });
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Path path={path} color="purple" />
    </Canvas>
  );
}

interpolateVector

Interpolates between 2D vectors.
import { interpolateVector } from "@shopify/react-native-skia";

const start = { x: 0, y: 0 };
const end = { x: 100, y: 100 };

const point = interpolateVector(0.5, [0, 1], [start, end]);
// point = { x: 50, y: 50 }

Using with Animations

Animated Position

import { useSharedValue, useDerivedValue } from "react-native-reanimated";
import { Canvas, Circle, interpolate } from "@shopify/react-native-skia";

const progress = useSharedValue(0);

const x = useDerivedValue(() => {
  return interpolate(progress.value, [0, 1], [50, 250]);
});

const y = useDerivedValue(() => {
  return interpolate(progress.value, [0, 1], [100, 300]);
});

<Circle cx={x} cy={y} r={30} color="blue" />

Animated Scale

const progress = useSharedValue(0);

const scale = useDerivedValue(() => {
  return interpolate(
    progress.value,
    [0, 0.5, 1],
    [1, 1.5, 1],
    "clamp"
  );
});

<Circle
  cx={150}
  cy={150}
  r={() => 50 * scale.value}
  color="green"
/>

Animated Rotation

const progress = useSharedValue(0);

const rotation = useDerivedValue(() => {
  return interpolate(
    progress.value,
    [0, 1],
    [0, Math.PI * 2]
  );
});

<Rect
  x={0}
  y={0}
  width={100}
  height={100}
  color="red"
  origin={{ x: 50, y: 50 }}
  transform={[{ rotate: rotation }]}
/>

Easing Functions

Combine with easing for natural motion:
import { Easing } from "react-native-reanimated";
import { interpolate } from "@shopify/react-native-skia";

const progress = useSharedValue(0);

const eased = useDerivedValue(() => {
  // Apply easing
  const easedProgress = Easing.bezier(0.25, 0.1, 0.25, 1)(progress.value);
  
  // Then interpolate
  return interpolate(easedProgress, [0, 1], [0, 300]);
});

Common Patterns

Fade In/Out

const opacity = useDerivedValue(() => {
  return interpolate(
    progress.value,
    [0, 0.3, 0.7, 1],
    [0, 1, 1, 0],
    "clamp"
  );
});

Bounce Effect

const scale = useDerivedValue(() => {
  return interpolate(
    progress.value,
    [0, 0.25, 0.5, 0.75, 1],
    [1, 1.2, 0.9, 1.05, 1]
  );
});

Parallax Scrolling

const scrollY = useSharedValue(0);

const backgroundY = useDerivedValue(() => {
  return interpolate(
    scrollY.value,
    [0, 500],
    [0, -100], // Slower movement
    "extend"
  );
});

const foregroundY = useDerivedValue(() => {
  return interpolate(
    scrollY.value,
    [0, 500],
    [0, -200], // Faster movement
    "extend"
  );
});

Performance Tips

  • Use useDerivedValue for interpolation in animations
  • Mark interpolation functions as worklets with "worklet"
  • Avoid creating new arrays in interpolate calls
  • Reuse output ranges when possible
  • For path interpolation, check isInterpolatable() first
  • Use "clamp" extrapolation to prevent unexpected values

Extrapolate Types

import { Extrapolate } from "@shopify/react-native-skia";

// Clamp to edges
Extrapolate.CLAMP

// Continue the pattern
Extrapolate.EXTEND

// Return input value unchanged
Extrapolate.IDENTITY

Build docs developers (and LLMs) love