Creating the Gooey Blob Effect in React Native using Skia

AKSHAY JADHAVAKSHAY JADHAV
9 min read

Ever seen those playful, blobby animations on websites where circles stick, stretch, and melt into each other like jelly? It’s like watching digital slime. That visual magic is called the gooey effect.

In the web world, this effect is often created with a mix of CSS filters, SVGs, and blurring tricks. But if you’re building with React Native, things get a little trickier. We don’t have CSS filters or native SVG filters out of the box.

So how do we make gooey magic happen in React Native?

How the Gooey Effect Works on the Web

The gooey effect on the web is mostly made using SVG filters, and the real star of the show is something called Gaussian blur.
Here’s the high-level idea:

Step 1: Gaussian Blur — Making Things Soft

When you blur a shape, it gets fuzzy around the edges. If two blurred shapes are close to each other, those fuzzy edges overlap and blend.

    <filter id="blur">
      <feGaussianBlur in="SourceGraphic" stdDeviation="10" />
    </filter>

This tells the browser: “Take the graphic (our blobs), and blur it with a softness of 10 pixels.”

Displayed two blue circles in an SVG, with a defined but unused blur filter.

The circles don’t physically touch, but the blur makes them look like they do.

Step 2: Color Matrix

So now we’ve got our circles blurred nice and soft around the edges. But right now, it’s just a visual overlap. The blobs look foggy, not gooey. To actually make them merge like sticky jelly we use something called a color matrix filter.

Here’s the updated SVG filter with the matrix added:

<filter id="goo">
  <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
  <feColorMatrix in="blur" mode="matrix"
    values="1 0 0 0 0
            0 1 0 0 0
            0 0 1 0 0
            0 0 0 20 -10" result="goo" />
</filter>

What’s a Color Matrix Actually Doing?

  • It’s basically a 4x5 math grid that lets you change the look of pixels.

  • The first 3 rows keep the colors (red, green, blue) unchanged.

  • The last row is where the effect happens:

    • 0 0 0 20 -10 boosts the alpha channel (transparency) Which makes the overlapping blurry parts look solid and connected.

It turns soft blurry overlaps into bold, sticky bridges between blobs — exactly the gooey merge we’re after!

Step 3: Putting the Blobs Back On Top

At this point, we’ve blurred the blobs (Step 1) and made them gooey using the color matrix (Step 2). But now they look like one weird melted shape we’ve lost the crisp edges of the original circles.

To bring back that clean look, we use a final SVG filter step: <feComposite>

This just blends the original shape back on top of the gooey version — like putting a shiny clean lid on a pot of slime.

<filter id="goo">
  <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
  <feColorMatrix in="blur" mode="matrix"
    values="1 0 0 0 0
            0 1 0 0 0
            0 0 1 0 0
            0 0 0 20 -10" result="goo" />
  <feComposite in="SourceGraphic" in2="goo" operator="atop" />
</filter>

In the next section, we’ll talk about how to recreate this same feel in React Native even though we don’t have these exact filter tools available.

Recreating the Gooey Effect in React Native with Skia

Installation & Setup (Using Expo)

Start by installing the necessary dependencies.

Using Yarn

yarn add @shopify/react-native-skia

Or with npm

npm install @shopify/react-native-skia

Then make sure to rebuild your app:

npx expo prebuild
npx expo run:ios   # or run:android

That’s it you now have a working Expo project with Skia support!

Note: If you’re using React Native CLI instead of Expo, you can follow the official Skia installation guide to set things up for your native project.

Setting Up the Skia Canvas

Now that we’ve installed everything, let’s get something on the screen. Before we dive into filters and effect, we’ll start by rendering some simple plain Circles using Skia.

Let’s update index.tsx with a simple canvas and a couple of overlapping circles:

import { Canvas, Circle } from "@shopify/react-native-skia";
import React from "react";
import { Dimensions, View } from "react-native";
const { width, height } = Dimensions.get("window");

const Index = () => {
  return (
    <View style={{ flex: 1 }}>
      <Canvas style={{ flex: 1 }}>
        <Circle cx={width / 2} cy={height / 2 - 20} r={50} color="#00BFFF" />
        <Circle cx={width / 2} cy={height / 2 + 80} r={50} color="#00BFFF" />
      </Canvas>
    </View>
  );
};

export default Index;
  • Canvas is your drawing area like a playground for blobs.

  • Circle draws a circle on that canvas. You can control its position with cx (center X), cy (center Y), and r (radius).

  • These two circles are overlapping slightly, setting the stage for gooeyness later.

Now that we got the basic circles on screen, it’s the time to implement the gooey effect using Skia’s Paint, Blur, and ColorMatrix.

Making It Gooey with Blur + ColorMatrix

What’s a Layer in Skia?

Skia lets us group multiple shapes together and apply visual effects to that group using the layer prop on a Group. This is super handy when you want to apply blur, filters, or blend modes across multiple objects.

In the following code we are wrapping our circles with <Group/> and passing a layer property which we will implement next.

import { Canvas, Circle, Group, Paint } from "@shopify/react-native-skia";
import React, { useMemo } from "react";
import { Dimensions, View } from "react-native";
const { width, height } = Dimensions.get("window");

const Index = () => {
  const layer = useMemo(() => {
    return <Paint></Paint>;
  }, []);
  return (
    <View style={{ flex: 1 }}>
      <Canvas style={{ flex: 1 }}>
        <Group layer={layer}>
          <Circle cx={width / 2} cy={height / 2 - 20} r={50} color="#00BFFF" />
          <Circle cx={width / 2} cy={height / 2 + 80} r={50} color="#00BFFF" />
        </Group>
      </Canvas>
    </View>
  );
};

export default Index;

Applying Blur Effect

We use Skia’s Blur inside a Paint to make the edges of our circles soft and fuzzy this is the first step toward the gooey effect.

const layer = useMemo(() => {
  return (
    <Paint>
      <Blur blur={10} />
    </Paint>
  );
}, []);

This makes each shape look a bit like it’s glowing. Showing that they melt together when they overlap.

Boost the Alpha with ColorMatrix

Blurring alone just softens the shapes, but to actually fuse them visually, we need to intensify the transparency (alpha) in the overlapping areas. We can do this by using ColorMatrix from Skia.

ColorMatrix: The matrix has five columns, each with a red, green, blue, and alpha component. You can use the matrix for tasks like creating a color transformation.

Here’s the updated layer code with the ColorMatrix added:

const layer = useMemo(() => {
    return (
      <Paint>
        <Blur blur={10} />
        <ColorMatrix
          matrix={[
            //
            1, 0, 0, 0, 0,
            //
            0, 1, 0, 0, 0,
            //
            0, 0, 1, 0, 0,
            //
            0, 0, 0, 20, 0,
          ]}
        />
      </Paint>
    );
  }, []);
  • The first three rows leave your colors (R, G, B) untouched

  • The last row boosts the alpha channel

this makes overlapping blurred parts more visible and starts giving us that gooey vibe. but you might still see soft shadows and faded halos around each shape. Like below:

Let’s fix this strange effect on edges.

Add Bias to Cut Off Low Alpha

The last column in the color matrix is called the bias it offsets the RGBA values after scaling. By subtracting a bias in our case -5, we remove the low-opacity parts of the blur. This gives us clean, sharp effect.

const layer = useMemo(() => {
    return (
      <Paint>
        <Blur blur={10} />
        <ColorMatrix
          matrix={[
            //
            1, 0, 0, 0, 0,
            //
            0, 1, 0, 0, 0,
            //
            0, 0, 1, 0, 0,
            //
            0, 0, 0, 20, -5,
          ]}
        />
      </Paint>
    );
  }, []);

Let’s Run It and See it in Action

# ios
npx expo run:ios

# android
npx expo run:android

You should see two blobs that beautifully melt together at their overlap. That’s your first working gooey effect in React Native using Skia.

Full Code

import {
  Blur,
  Canvas,
  Circle,
  ColorMatrix,
  Group,
  Paint,
} from "@shopify/react-native-skia";
import React, { useMemo } from "react";
import { Dimensions, View } from "react-native";
const { width, height } = Dimensions.get("window");

const Index = () => {
  const layer = useMemo(() => {
    return (
      <Paint>
        <Blur blur={10} />
        <ColorMatrix
          matrix={[
            //
            1, 0, 0, 0, 0,
            //
            0, 1, 0, 0, 0,
            //
            0, 0, 1, 0, 0,
            //
            0, 0, 0, 20, -5,
          ]}
        />
      </Paint>
    );
  }, []);
  return (
    <View style={{ flex: 1 }}>
      <Canvas style={{ flex: 1 }}>
        <Group layer={layer}>
          <Circle cx={width / 2} cy={height / 2 - 20} r={50} color="#00BFFF" />
          <Circle cx={width / 2} cy={height / 2 + 80} r={50} color="#00BFFF" />
        </Group>
      </Canvas>
    </View>
  );
};

export default Index;

Bonus: Make It Wiggle (with Reanimated)

Let’s add a tiny bit of life to our blobs using react-native-reanimated. We’ll animate the positions to give them a natural wiggle motion kind of like floating jelly.

Setup Animated Values

  // Create animated shared values
  const topCircleY = useSharedValue(height / 2 - 200);
  const bottomCircleY = useSharedValue(height / 2 + 200);
  const topCircleX = useSharedValue(width / 2);
  const bottomCircleX = useSharedValue(width / 2);

Animate Positions

We can animate them using withRepeat + withTiming and pass those into Skia Circle components.

  React.useEffect(() => {
    // Vertical animation
    topCircleY.value = withRepeat(
      withTiming(height / 2, {
        duration: 4000,
      }),
      -1,
      true
    );
    // Vertical animation
    bottomCircleY.value = withRepeat(
      withTiming(height / 2, {
        duration: 4000,
      }),
      -1,
      true
    );

    // Horizontal animation
    topCircleX.value = withRepeat(
      withTiming(width / 2 + 30, {
        duration: 2000,
      }),
      -1,
      true
    );
    // Horizontal animation
    bottomCircleX.value = withRepeat(
      withTiming(width / 2 - 30, {
        duration: 2000,
      }),
      -1,
      true
    );
  }, []);

The above code

  • Moves both blobs back toward the center (vertically)

  • Adds gentle side-to-side motion (horizontally)

  • All animations loop back and forth forever (-1, true)

Use Animated Values in Skia Circle

Once topCircleX, topCircleY, etc. are animating, just pass them directly into Skia Circle components they accept SharedValue types out of the box.

<Canvas style={{ flex: 1 }}>
  <Group layer={layer}>
    <Circle cx={topCircleX} cy={topCircleY} r={50} color="#00BFFF" />
    <Circle cx={bottomCircleX} cy={bottomCircleY} r={50} color="#00BFFF" />
  </Group>
</Canvas>

Final Result

Wrapping Up

We took a fun visual effect often seen in web design and recreated it in React Native using Skia, ColorMatrix, and a little help from Reanimated.

0
Subscribe to my newsletter

Read articles from AKSHAY JADHAV directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

AKSHAY JADHAV
AKSHAY JADHAV

Software Engineer