Veltrix

skia shaders

Soft Noise Surface

A subtle organic background with configurable grain, amplitude, and reduced motion.

AppleiOSAndroidAndroidExpoExpo Go
skiareanimated
GitHubOpen in GitHub

Installation

Install the dependencies, then copy the files for your preferred renderer.

pnpm add @shopify/react-native-skia react-native-reanimated

Install tailwind variants

pnpm add tailwind-variants

Usage

SoftNoiseSurfaceExample.tsx
import { Text, View } from "react-native";import { SoftNoiseSurface } from "@animations/ui/components/skia-shaders/soft-noise-surface/uniwind";export function SoftNoiseSurfaceExample() {  return (    <SoftNoiseSurface      classNames={{ content: "px-6 py-8" }}      tint="#0ea5e9"    >      <View>        <Text className="text-2xl font-extrabold text-white">Quietly alive</Text>      </View>    </SoftNoiseSurface>  );}

Component files

5 files

soft-noise-surface/config.ts
export const SOFT_NOISE_CONFIG = {  amplitude: 0.92,  backgroundColor: "#121218",  borderRadius: 24,  duration: 6_200,  frequency: 0.026,  intensity: 0.3,  octaves: 2,  overscan: 40,  seed: 8,  tint: "#8b7cf6",} as const;export const SOFT_NOISE_GRAYSCALE_MATRIX = [  0.2126, 0.7152, 0.0722, 0, 0,  0.2126, 0.7152, 0.0722, 0, 0,  0.2126, 0.7152, 0.0722, 0, 0,  0, 0, 0, 1, 0,];
soft-noise-surface/noise-layer.tsx
import {  BlendColor,  Canvas,  ColorMatrix,  Fill,  FractalNoise,  Image,  Rect,  useCanvasSize,  useClock,  useTexture,  type SkImage,} from "@shopify/react-native-skia";import { useMemo } from "react";import { StyleSheet } from "react-native";import {  useDerivedValue,  useReducedMotion,  type SharedValue,} from "react-native-reanimated";import {  SOFT_NOISE_CONFIG,  SOFT_NOISE_GRAYSCALE_MATRIX,} from "./config";import type { SoftNoiseSurfaceBaseProps } from "./types";type NoiseTextureProps = {  amplitude: number;  animated: boolean;  duration: number;  frequency: number;  height: number;  intensity: number;  octaves: number;  seed: number;  tint: string;  width: number;};function NoiseTexture({  amplitude,  animated,  duration,  frequency,  height,  intensity,  octaves,  seed,  tint,  width,}: NoiseTextureProps) {  const textureWidth =    width + SOFT_NOISE_CONFIG.overscan * 2;  const textureHeight =    height + SOFT_NOISE_CONFIG.overscan * 2;  const textureSize = useMemo(    () => ({      height: textureHeight,      width: textureWidth,    }),    [textureHeight, textureWidth],  );  const texture = useTexture(    <Rect      height={textureHeight}      width={textureWidth}      x={0}      y={0}    >      <FractalNoise        freqX={frequency}        freqY={frequency * 0.82}        octaves={octaves}        seed={seed}        tileHeight={textureHeight}        tileWidth={textureWidth}      />      <BlendColor color={tint} mode="color">        <ColorMatrix          matrix={SOFT_NOISE_GRAYSCALE_MATRIX}        />      </BlendColor>    </Rect>,    textureSize,    [      frequency,      octaves,      seed,      textureHeight,      textureWidth,      tint,    ],  );  return (    <NoiseTextureImage      amplitude={amplitude}      animated={animated}      duration={duration}      height={textureHeight}      image={texture}      intensity={intensity}      width={textureWidth}    />  );}type NoiseTextureImageProps = {  amplitude: number;  animated: boolean;  duration: number;  height: number;  image: SharedValue<SkImage | null>;  intensity: number;  width: number;};function NoiseTextureImage({  amplitude,  animated,  duration,  height,  image,  intensity,  width,}: NoiseTextureImageProps) {  if (animated) {    return (      <AnimatedNoiseTextureImage        amplitude={amplitude}        duration={duration}        height={height}        image={image}        intensity={intensity}        width={width}      />    );  }  return (    <Image      height={height}      image={image}      opacity={Math.min(1, Math.max(0, intensity))}      width={width}      x={-SOFT_NOISE_CONFIG.overscan}      y={-SOFT_NOISE_CONFIG.overscan}    />  );}type AnimatedNoiseTextureImageProps = Omit<  NoiseTextureImageProps,  "animated">;function AnimatedNoiseTextureImage({  amplitude,  duration,  height,  image,  intensity,  width,}: AnimatedNoiseTextureImageProps) {  const clock = useClock();  const x = useDerivedValue(() => {    const time = clock.get() / Math.max(1, duration);    const drift =      Math.sin(time * Math.PI * 2 * 0.71 + 0.8) *        0.58 +      Math.sin(time * Math.PI * 2 * 1.37 + 2.1) *        0.27 +      Math.sin(time * Math.PI * 2 * 0.19) * 0.15;    return (      -SOFT_NOISE_CONFIG.overscan +      drift *        SOFT_NOISE_CONFIG.overscan *        Math.min(1, Math.max(0, amplitude))    );  });  const y = useDerivedValue(() => {    const time = clock.get() / Math.max(1, duration);    const drift =      Math.cos(time * Math.PI * 2 * 0.53 + 1.4) *        0.55 +      Math.sin(time * Math.PI * 2 * 1.11 + 0.3) *        0.3 +      Math.cos(time * Math.PI * 2 * 0.23 + 2.8) *        0.15;    return (      -SOFT_NOISE_CONFIG.overscan +      drift *        SOFT_NOISE_CONFIG.overscan *        Math.min(1, Math.max(0, amplitude))    );  });  return (    <Image      height={height}      image={image}      opacity={Math.min(1, Math.max(0, intensity))}      width={width}      x={x}      y={y}    />  );}type SoftNoiseLayerProps = Pick<  SoftNoiseSurfaceBaseProps,  | "amplitude"  | "animated"  | "backgroundColor"  | "duration"  | "frequency"  | "intensity"  | "octaves"  | "paused"  | "seed"  | "tint">;export function SoftNoiseLayer({  amplitude = SOFT_NOISE_CONFIG.amplitude,  animated = true,  backgroundColor = SOFT_NOISE_CONFIG.backgroundColor,  duration = SOFT_NOISE_CONFIG.duration,  frequency = SOFT_NOISE_CONFIG.frequency,  intensity = SOFT_NOISE_CONFIG.intensity,  octaves = SOFT_NOISE_CONFIG.octaves,  paused = false,  seed = SOFT_NOISE_CONFIG.seed,  tint = SOFT_NOISE_CONFIG.tint,}: SoftNoiseLayerProps) {  const {    ref,    size: { height, width },  } = useCanvasSize();  const reducedMotion = useReducedMotion();  const shouldAnimate = animated && !paused && !reducedMotion;  const drawingProps = {    amplitude,    animated: shouldAnimate,    duration,    frequency,    height,    intensity,    octaves,    seed,    tint,    width,  };  return (    <Canvas      pointerEvents="none"      ref={ref}      style={StyleSheet.absoluteFill}    >      <Fill color={backgroundColor} />      {width > 0 && height > 0 && (        <NoiseTexture {...drawingProps} />      )}    </Canvas>  );}
soft-noise-surface/types.ts
import type { ReactNode } from "react";export type SoftNoiseSurfaceBaseProps = {  accessibilityLabel?: string;  amplitude?: number;  animated?: boolean;  backgroundColor?: string;  borderRadius?: number;  children: ReactNode;  duration?: number;  frequency?: number;  intensity?: number;  octaves?: number;  paused?: boolean;  seed?: number;  tint?: string;};
soft-noise-surface/uniwind/index.tsx
import { View } from "react-native";import type { SlotsToClasses } from "#shared/types/slots";import { SOFT_NOISE_CONFIG } from "../config";import { SoftNoiseLayer } from "../noise-layer";import type { SoftNoiseSurfaceBaseProps } from "../types";import {  softNoiseSurfaceVariants,  type SoftNoiseSurfaceSlot,} from "./variants";export type SoftNoiseSurfaceProps =  SoftNoiseSurfaceBaseProps & {    className?: string;    classNames?: SlotsToClasses<SoftNoiseSurfaceSlot>;  };export function SoftNoiseSurface({  accessibilityLabel,  amplitude,  animated,  backgroundColor,  borderRadius = SOFT_NOISE_CONFIG.borderRadius,  children,  className,  classNames,  duration,  frequency,  intensity,  octaves,  paused,  seed,  tint,}: SoftNoiseSurfaceProps) {  const { container, content } = softNoiseSurfaceVariants();  return (    <View      accessibilityLabel={accessibilityLabel}      accessible={accessibilityLabel !== undefined}      className={container({        className: [className, classNames?.container],      })}      style={{ borderRadius }}    >      <SoftNoiseLayer        amplitude={amplitude}        animated={animated}        backgroundColor={backgroundColor}        duration={duration}        frequency={frequency}        intensity={intensity}        octaves={octaves}        paused={paused}        seed={seed}        tint={tint}      />      <View        className={content({          className: classNames?.content,        })}      >        {children}      </View>    </View>  );}
soft-noise-surface/uniwind/variants.ts
import { tv } from "tailwind-variants";export const softNoiseSurfaceSlots = {  container: "overflow-hidden rounded-3xl",  content: "",} as const;export type SoftNoiseSurfaceSlot =  keyof typeof softNoiseSurfaceSlots;export const softNoiseSurfaceVariants = tv({  slots: softNoiseSurfaceSlots,});

API reference

Generated directly from the exported component props.

SoftNoiseSurface