# Funhouse Portrait

> Inflate noses, squeeze faces and twist smiles with a playful three.js portrait filter that springs back on release.

Canonical: https://gsapvault.com/effects/funhouse-portrait
Live demo: https://gsapvault.com/demos/funhouse-portrait/index.html

| Property | Value |
|----------|-------|
| Type | effect |
| Tier | paid |
| Price | £5 |
| Difficulty | advanced |
| Plugins | Core GSAP only |
| Techniques | webgl-shader, fisheye, hover-effect, draggable |
| Uses Lenis | No |

## Overview

A playful portrait filter effect that turns a photograph into a movable funhouse mirror. Hold over a nose, an eye or a smile to grow a smooth local distortion, then drag it across the face. Three.js renders the photo lens; GSAP handles the press, elastic release and transitions between three distinct filters.

## Features

- Inflate, Squeeze and Twist lenses with visibly different shapes
- Press-and-hold intensity with a bouncy release and subtle radial ripple
- Touch dragging and a full keyboard interaction
- Two bundled portrait photographs with a compact image switch
- Configurable lens radius, strength and optional mobile drift
- Natural photo colour with no lighting or colour-fringe overlay
- Composed photo fallback for reduced motion, no JavaScript and unavailable WebGL
- Documented cleanup for single-page application routes

## Use Cases

- Creative studio portraits with an unexpected personal touch
- Playful campaign microsites with an immediately shareable interaction
- Photo booths with a choice of exaggerated lens treatments
- Interactive team pages with memorable portrait details

## Vibe-Code Ready Setup

This effect includes `START-HERE-AI.md`, a product-specific copy-paste setup prompt for Cursor, Claude Code, ChatGPT, GitHub Copilot, Windsurf, and other coding assistants. It tells the assistant to inspect the existing stack, integrate the supplied files, preserve the design, scope selectors, retain accessibility and responsive behaviour, add framework-appropriate GSAP cleanup, and report what it tested.

[How AI-assisted setup works](https://gsapvault.com/vibe-coding)

## How It Works

A three.js fragment shader remaps a small region of the photograph through an analytic lens while the rest of the image remains intact. Pointer and touch input position the lens, and GSAP animates its intensity and elastic return. The same source photograph provides a static fallback when motion or WebGL is unavailable.

## Documentation

How this effect works and how it goes into a page. The reference you use once you own the files (worked examples, events, the programmatic API, the class list) ships with the download.

### Quick Start

**1. Add to your HTML `<head>`:**

_Code snippet omitted: it ships with the download._

The synchronous probe prevents a flash of the source photo while the module loads. Keep the module error handler in step 3 so a failed network request restores the photograph.

**2. Add to your `<body>`:**

_Code snippet omitted: it ships with the download._

**3. Add before closing `</body>`:**

_Code snippet omitted: it ships with the download._

Already using three.js? Reuse your import and assign it to `window.THREE` before loading the effect script. Keep one three.js version per page. Use a local HTTP server; ES modules should not be opened through `file://`.

### Using It With Your Own Design

The script needs a `[data-funhouse]` wrapper, a sized `[data-funhouse-stage]` button, and `[data-funhouse-source]` containing at least one image. Optional mode buttons and the next-photo button must be inside the wrapper. The script queries these data hooks, so the page shell, typography, colours and toolbar styling can all be replaced.

Keep the stage relatively positioned with a definite height, clipped overflow and `touch-action: pan-y`. Keep its canvas absolutely positioned, full-size and `pointer-events: none`. Source images use `object-fit: cover`, matching the shader's cover crop. Disable native image dragging. Hide the source under `html.gl` and `.is-live`, never simply because JavaScript is present; reduced motion must restore it. The supplied fallback hides motion-only controls until the root has `data-ready="true"`.

### Options

Set options on `[data-funhouse]` before initialisation.

| Attribute | Values | Default | Description |
|---|---|---|---|
| `data-strength` | 0–1.2 | 1 | Maximum held intensity; 0 disables deformation. |
| `data-radius` | 0.1–0.5 | 0.30 | Lens radius as a fraction of stage height. |
| `data-drift` | 0 or 1 | 1 | Gentle automatic lens drift on coarse pointers after three idle seconds. Set 0 for a completely still resting photograph. |
| `data-funhouse-mode` | inflate, squeeze, twist | — | Hook on a mode button. The initial mode is Inflate. |
| `data-funhouse-next` | Presence | — | Hook on the optional next-photo button. |

### Choosing Your Images

Choose a sharp, front-facing head-and-shoulders portrait with visible eyes, nose and mouth. Crop close enough that individual features are easy to target. A simple background keeps the lens readable; distant faces and soft-focus images lose the comic detail. The filter uses ordinary photographs and does not detect faces or require camera access.

Two portraits are included so you can try the filter on different facial features. The starting portrait is a generated studio image in a striped turtleneck; its master and generation prompt are included in `assets/img-src/`. Your replacement images stay in natural colour: this shader does not require a baked grade. The cover crop adapts to the stage ratio, so check the entire face remains accessible at your widest and narrowest layouts.

### Accessibility

The portrait is a native button. Hold Space or Enter to apply the lens, use arrow keys to move it and release to settle. Tab reaches every mode and photo button, with visible focus outlines. Mouse and touch use the same press-and-drag interaction; vertical touch gestures remain available for scrolling.

Reduced motion shows the original photograph and hides the supplied motion controls. No JavaScript and unavailable WebGL use the same composed photo fallback. No device orientation permission or camera permission is needed.

### Browser Support

Requires a browser with ES modules, import maps and WebGL for the interactive filter. Current Chromium, Firefox and Safari support these features. If WebGL is unavailable, the photo remains visible. Serve photos from the same origin or configure CORS on their host.

### Performance

One image plane and one shader pass; no physics mesh or post-processing chain. Device pixel ratio is capped at 2 for fine pointers and 1.5 for coarse pointers. The loop pauses offscreen and when the document is hidden. To reduce GPU cost first, lower these caps in the renderer setup; radius and strength change appearance without reducing the number of shaded pixels. Use appropriately sized WebP photos to reduce loading cost.

### Dependencies

| Dependency | Role |
|---|---|
| GSAP 3.12+ (demo uses 3.15.0) | Press intensity, mode blending, elastic release, motion preference handling and teardown. No plugins. |
| three.js | WebGL renderer, image textures and the analytic photo-lens shader; demo pins the ES module in the import map. |

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `START-HERE-AI.md`: product-specific copy-paste prompt for AI-assisted setup
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium effect. The standard license costs £5 one-time and covers unlimited personal and commercial projects with no attribution required. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy Funhouse Portrait](https://gsapvault.com/effects/funhouse-portrait)
- [The Vault (£99 one-time, best value): everything in the catalogue, including future releases](https://gsapvault.com/effects)

## Judge the Code Quality First

These related effects are free with complete source published, written to the same production standard (cleanup functions, reduced-motion support, framework-agnostic):

- [Hover Underline](https://gsapvault.com/effects/hover-underline.md): Four material link underlines (an exit-through line, marker sweep, hand-drawn wave, and an endlessly travelling wave) with coordinated type and active-index responses.
- [3D Card Flip Gallery](https://gsapvault.com/effects/3d-card-flip.md): Tactile GSAP 3D cards with deep perspective, reactive edge lighting and shifting shadows. Flip on hover, keyboard focus or tap, with grouped auto-close.
- [Image Clip Reveal](https://gsapvault.com/effects/image-clip-reveal.md): A cinematic image reveal where a directional polygon aperture opens as the photograph settles from a restrained Ken Burns scale and its caption lands.

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
