diff --git a/.storybook/stories/Example.stories.tsx b/.storybook/stories/Example.stories.tsx
new file mode 100644
index 0000000..abb4b93
--- /dev/null
+++ b/.storybook/stories/Example.stories.tsx
@@ -0,0 +1,48 @@
+import React, { memo } from 'react'
+import * as THREE from 'three'
+import type { Meta, StoryObj } from '@storybook/react'
+import { Box, useTexture } from '@react-three/drei'
+
+import { Setup } from '../Setup'
+import { EffectComposer, Example } from '../../src'
+import { BlendFunction } from 'postprocessing'
+
+// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
+const meta = {
+ title: 'Effect/Example',
+ component: Example,
+ decorators: [(Story) => {Story()}],
+ tags: ['autodocs'],
+ argTypes: {
+ blendFunction: {
+ control: 'select',
+ options: Object.keys(BlendFunction),
+ mapping: Object.keys(BlendFunction).reduce((acc, k) => {
+ acc[k] = BlendFunction[k]
+ return acc
+ }, {}),
+ },
+ opacity: { control: { type: 'range', min: 0, max: 1, step: 0.001 } },
+ color: { control: { type: 'color' } },
+ },
+} satisfies Meta
+
+export default meta
+type Story = StoryObj
+
+// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
+export const Primary: Story = {
+ render: ({ color, ...args }) => (
+ <>
+
+
+
+
+
+ >
+ ),
+ args: {
+ blendFunction: BlendFunction.ADD,
+ color: 'red' as unknown as THREE.Color,
+ },
+}
diff --git a/src/effects/Example.tsx b/src/effects/Example.tsx
new file mode 100644
index 0000000..b3ef27b
--- /dev/null
+++ b/src/effects/Example.tsx
@@ -0,0 +1,77 @@
+import * as THREE from 'three'
+import { useRef } from 'react'
+import { useFrame, useThree } from '@react-three/fiber'
+import { BlendFunction, Effect } from 'postprocessing'
+
+import { wrapEffect } from '../util'
+
+//
+// Effect
+//
+
+const ExampleShader = {
+ fragment: `
+ uniform vec3 color;
+ uniform float time;
+
+ void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) {
+ outputColor = vec4(color, 0.5 + (cos(time) / 2.0 + 0.5));
+ }
+ `,
+}
+
+type ExampleEffectOptions = {
+ /** The color for this effect */
+ color: THREE.Color
+ /** The blend function of this effect */
+ blendFunction?: BlendFunction
+}
+
+export class ExampleEffect extends Effect {
+ constructor({ color, blendFunction }: ExampleEffectOptions) {
+ super('LensFlareEffect', ExampleShader.fragment, {
+ blendFunction,
+ uniforms: new Map([
+ ['color', new THREE.Uniform(color)],
+ ['time', new THREE.Uniform(0)],
+ ]),
+ })
+ }
+
+ update(_renderer: any, _inputBuffer: any, deltaTime: number) {
+ const time = this.uniforms.get('time')
+ if (time) {
+ time.value += deltaTime
+ }
+ }
+}
+
+//
+// Component
+//
+
+const ExampleWrapped = wrapEffect(ExampleEffect)
+
+type ExampleProps = React.ComponentPropsWithoutRef & {
+ /** mouse */
+ mouse?: boolean
+}
+
+export const Example = ({ mouse = false, ...props }: ExampleProps) => {
+ const pointer = useThree(({ pointer }) => pointer)
+
+ const ref = useRef(null)
+
+ useFrame(() => {
+ if (!mouse) return
+ if (!ref?.current) return
+
+ const uColor = ref.current.uniforms.get('color')
+ if (!uColor) return
+
+ uColor.value.r = pointer.x / 2.0 + 0.5
+ uColor.value.g = pointer.y / 2.0 + 0.5
+ })
+
+ return
+}
diff --git a/src/index.tsx b/src/index.tsx
index c13a02f..71816ee 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -12,6 +12,7 @@ export * from './effects/ColorDepth'
export * from './effects/Depth'
export * from './effects/DepthOfField'
export * from './effects/DotScreen'
+export * from './effects/Example'
export * from './effects/Glitch'
export * from './effects/GodRays'
export * from './effects/Grid'