# Optional filters
[Full-page experience](/experience/optional-filters/) · [Open playground](/playground/#/examples/composition/optional-filters)

Import a blur factory, animate its input, and keep the original click geometry.

## Try it

Toggle blur, change its radius, and click inside and just outside the orange outline.

## What to learn

- Use an explicitly imported appearance-only filter.
- Observe unchanged target geometry while blur changes.

## Integration boundaries

- The filter changes pixels only. The orange outline stays aligned with the click target.
- Importing one filter does not require the other effects.

This is a Pibbl Canvas example. Its mount owns its runtime resources; call its controller's dispose method when removing it. The preview starts automatically unless reduced motion is enabled; Stop disposes the mount. Preserve the source's logical canvas size and aspect ratio. Native HTML is not captured by Canvas filters.

## Related APIs

- [Group](/reference/components/group/)
- [Rectangle](/reference/components/rectangle/)
- [Text](/reference/components/text/)
- [PibblFilter](/reference/types/filters/)
- [useSignal](/reference/hooks/use-signal/)

## Complete source

Host setup for this source: use a canvas with width 720 and height 420, compile with `jsx: "react-jsx"` and `jsxImportSource: "@pibbl/core"`, import its default `mount`, and call `const controller = mount(canvas)` after attaching the canvas. Call `controller.dispose()` before removing it.

### optional-filters.example.tsx

```tsx
import { pibbl, Group, Rectangle, Text, useSignal, type PibblController } from '@pibbl/core';
import { blur } from '@pibbl/core/filters';

function OptionalFilters() {
  const enabled = useSignal(true);
  const radius = useSignal(4);
  const clicks = useSignal(0);
  return (
    <Group>
      <Rectangle style={{ width: 720, height: 420, fill: '#f7f0df' }} />
      <Text style={{ left: 40, top: 48, font: '800 27px sans-serif', fill: '#17211d' }}>
        Appearance changes. Targets stay put.
      </Text>
      <Text style={{ left: 40, top: 78, font: '14px sans-serif', fill: '#52615c' }}>
        Toggle blur, then click the teal square or its soft edge.
      </Text>
      <Group style={{ filter: enabled.get() ? blur({ radius }) : [] }}>
        <Rectangle
          style={{ left: 240, top: 122, width: 240, height: 160, fill: '#127e79', cursor: 'pointer' }}
          onClick={() => clicks.update(value => value + 1)}
        />
        <Text pointerEvents="none" style={{ left: 360, top: 211, textAlign: 'center', font: '800 23px sans-serif', fill: '#ffffff' }}>
          CLICK ME
        </Text>
      </Group>
      <Rectangle pointerEvents="none" style={{ left: 240, top: 122, width: 240, height: 160, stroke: '#d1782b', strokeWidth: 2 }} />
      <Rectangle style={{ left: 40, top: 330, width: 190, height: 48, fill: '#17211d', cursor: 'pointer' }} onClick={() => enabled.update(value => !value)} />
      <Text pointerEvents="none" style={{ left: 135, top: 360, textAlign: 'center', font: '700 15px sans-serif', fill: '#ffffff' }}>
        {enabled.get() ? 'TURN BLUR OFF' : 'TURN BLUR ON'}
      </Text>
      <Rectangle style={{ left: 250, top: 330, width: 190, height: 48, fill: '#ffffff', stroke: '#17211d', strokeWidth: 1, cursor: 'pointer' }} onClick={() => radius.update(value => value === 4 ? 12 : 4)} />
      <Text pointerEvents="none" style={{ left: 345, top: 360, textAlign: 'center', font: '700 15px sans-serif', fill: '#17211d' }}>
        {`BLUR RADIUS ${radius.get()}`}
      </Text>
      <Text pointerEvents="none" style={{ left: 475, top: 360, font: '700 16px monospace', fill: '#17211d' }}>
        {`CLICKS ${clicks.get()}`}
      </Text>
      <Text pointerEvents="none" style={{ left: 40, top: 403, font: '12px sans-serif', fill: '#52615c' }}>
        The orange outline marks the original target geometry.
      </Text>
    </Group>
  );
}

export default function mount(canvas: HTMLCanvasElement): PibblController {
  return pibbl(canvas, <OptionalFilters />);
}

```

## Documentation version

Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.
