The best camera and video effects are built by individual developers on .show. These contributors have created real-time interactive filters, AR effects, video transitions, styled templates and more using innovative technologies like OpenGL, Computer Vision, ARKit, Metal, openframeworks, and more.

This guide will help developers integrate their custom camera effects in the .show app.

If you're interested in getting credited and paid for your contributions and would like to learn more about the Camera Developer Program, you can submit your work for approval


Filters Quick Start

Camera Developer Sandbox (Xcode)

Open the Camera Developer Sandbox in XCode.

Duplicate the files “TestFilter.swift” and “TestKernel.metal”.

Rename the files to your chosen name while keeping the Filter suffix.

Replace the references to TestFilter and TestKernel in both the files to their new names.

Add an instance of your filter to FilterHelper, following the example of TestFilter.

You can add custom parameters to your shader by reading the following Parameters section of this guide.

Make a build and test your shader 💫

The icon for your filter must be named the same as your title.


Parameters are initialized in your swift file and define the inputs and outputs corresponding to the parameters of your Metal shader’s main function. Each parameter specifies a “targetIndex” which is how it relates to the metal shader function parameters.

Setting the name (: String) of the parameter as one of the constant values defined in “ParameterConstantKeys” will be automatically populated with the value of the relevant data.

User Controlled Parameter

The .show app provides control to the user so they can explore the possibility space of your filter.

A Vector2Parameter defined with the key ParameterConstantKeys.UserControlParameter will automatically have values mapped from the touch input of the screen to the variable within the defined bounds.

let vector2s : [String:Vec2Parameter] =
[ ParameterConstantKeys.UserControlParameter:
Vec2Parameter(idx: 1,
name: ParameterConstantKeys.UserControlParameter,
defaultVal: vector_float2(1.0, 0.5),
bounds: vector_float4(0.0, 2.0, 0.0, 1.0) )

Texture Parameters

If your texture doesn’t get updated until after the first render, use TextureLoader.shared.blankTexture as a starting value on initialization.

Source TextureSourceTextureRequired to specify source
Destination TextureDestinationTextureRequired to specify destination
Last TextureLastTextureOptional for sampling the previous buffer

Float parameters

Contain a range. If you pass time, pass nil or a range of length zero and the value will be unbounded.

Int Parameters

You can define int parameters like this:

let ints : [String:IntParameter] =
[parameterKeyIsVertical: IntParameter(idx: 0, name: parameterKeyIsVertical, defaultVal: 0)]

Audio Texture Parameter

.Show supports passing the microphone FFT array to the shader for audio-reactive effects. In the init function of your filter, just call createAudioBuffer() with the buffer index you want the float array to be passed to in your shader. This has to be called after the parameters are assigned. An example of this can be found in the Filter Dev Sandbox Invert kernel.

self.createAudioBuffer(bufferIndex: 1)

Other data types

If you want to set other data types, you must override the updateUniforms function and set them on the encoder yourself.

Setting Parameters Example

init(device: MTLDevice) {
super.init(device: device, name: "FeedbackKernel")
let textures : [String:TextureParameter] =
TextureParameter(name:ParameterConstantKeys.SourceTexture, texture: nil, targetIndex: 0),
TextureParameter(name:ParameterConstantKeys.DestinationTexture, texture: nil, targetIndex: 1),
TextureParameter(name:ParameterConstantKeys.LastTexture, texture: TextureLoader.shared.blankTexture, targetIndex: 2)]
let floats : [String:FloatParameter] =
FloatParameter(idx: 0, name: ParameterConstantKeys.Time, defaultVal: 0.0, range: (0.0,0.0)),
FloatParameter(idx: 1, name: "param", defaultVal: 0.0, range: (0.0,0.0))]
self.parameters = FilterParameters(textures: textures, floats: floats )

Overridable Functions

When you subclass Filter, you can override the following functions

[Required] func init(device: MTLDevice)

This function calls super.init(device: MTLDevice, name: String) with the name of the metal kernel associated with your file. Initialize self.parameters here.


Called when the filter is selected.


Called when the filter is navigated away from.

func preRender(commandBuffer: MTLCommandBuffer, _width:Int, _height:Int)

Called before the effect is rendered

func postRender()

Called after the effect is rendered

func updateUniforms(encoder: MTLComputeCommandEncoder,
sourceTexture: MTLTexture,
destinationTexture: MTLTexture,
time: Float)

Called before the effect is rendered. Use this if you need to update uniforms manually.

Metal Includes

We provide some useful functions to save you time implementing them.


float random(float n);
float noise1d(float p);
float noise2dTex(texture2d tex, metal::sampler colorSampler, float2 pos);
float fbm(float pos);
float fbm(texture2d tex, metal::sampler colorSampler, float2 pos);


float3 rgb2hsv(float3 col);
float3 hsv2rgb(float3 col);


float3 hue(float3 col, float shift);


float3 blendSoftLight(float3 base, float3 blend);
float3 overlay(float3 base, float3 overlay);


float sobelSample(texture2d<float, access::read> inTexture, uint2 p, int2 texel, float scale);


Create a .zip named with the format FilterName_Author with all of the files required for your effect in a subdirectory with the same format.

Ex filter “Hue Cycle” by author “Connor Bell”: will extract to have top level directory named “HueCycle_Connorbell”

Submit your work for approval here.

Help & Advice

If you have any questions or have any suggestions for how we can improve this documentation, please email us at