I implemented the spherical Lissajous curve as a shader with Shadertoy.
Code-On
Art and Code
Tuesday, September 2, 2025
Sunday, August 24, 2025
Procedural Texture Filter System Variables
Affinity Photo's Procedural Texture Filter System Variables
x, y, rx, ry, w, h, pi
x is the horizontal pixel coordinate position beginning with 0 at the left edge of the document and increasing toward the right by 1 for each pixel.
y is the vertical pixel coordinate position beginning with 0 at the top edge of the document and increasing downward by 1 for each pixel.
rx and ry are relative pixel coordinates. These coordinates allow repositioning of the coordinate system in two ways. Double-clicking the mouse on the document moves the origin (0,0) to the current position of the mouse. Click and dragging moves the coordinate system from the current position to the position you drag it to.
w is the width of the document in pixels, ie 1024
h is the height of the document in pixels, ie 768
pi is the geometric constant 3.14159265...
RGBA inputs
R contains the pixel information for the red channel in the document. It is not possible to access current image pixels individually from this variable. It is possible to modify the image using the channel outputs.
G contains the pixel information for the green channel.
B contains the pixel information for the blue channel.
A contains the pixel information for the alpha channel.
If the document is in CMYK mode, the letters C, M, Y, and K represent the color pixel information for the document. Note that you won't be able to access your RGB filters if the document is set to CMYK color mode.
p cannot be used as a custom input or as a variable name. p followed by a digit may not be used as a custom input but may be used to define a var variable. This includes p1, p2a, p304, etc.
Variable names must begin with a letter may only consist of numbers and letters, and _ (underscore) characters. Upper and lower case letters are allowed and a1 is not the same variable as A1. Space is not allowed nor are other special characters.
A 1024 x 768 landscape document would have an x value ranging from 0 to 1024, with 0 just to the left of the first pixel and 1024 just to the right of the last pixel. A value of x centered on the first pixel would be .5.
Friday, August 22, 2025
Affinity Photo Procedural Texture Filter: Seigaiha
Seigaiha Pattern
- var v=vec2(x,y)*a/w+vec2(of/2,of/4); // Coordinates with offset
- var v1=fraction(v)-.5; // Mask 1 Coordinates
- var m1=smoothstepn(.5-s1/10,.5+s1/10,length(v1))*osci(v.y); // Mask 1 Positive
- var v2=fraction(v+vec2(0,.5))-.5; // Mask 2 Coordinates
- var m2=1-smoothstepn(.5-s1/10,.5+s1/10,length(v2))*osci(v.y-.5); // Mask 2 Negative
- var v3=fraction(v+vec2(.5,.75))-.5; // Mask 3 Coordinates
- var m3=1-smoothstepn(.5-s1/10,.5+s1/10,length(v3))*osci(v.y-.25); // Mask 3 Negative
- m1*m2*m3*smoothosc(b*length(v1),s2) // Mask with lines
Sunday, June 29, 2025
Affinity Photo Procedural Texture Filter
Affinity Photo Procedural Texture Filter Examples
List of PTF Functions
Sign Related Functions
abs sign copysign
Rounding Related Functions
ceil floor fmod fraction irem idiv round roundup rounddown trunc truncate whole
Trig Functions
sin cos tan acos asin atan atan2 pi
Power Related Functions
pow powr sq sqrt
Statistical Functions
average max mid min
Specialized Math Functions
dim fma rgbtoi
Numeric Range Related Functions
tocui tohcui clamp clampmin clampmax saturate
Geometry and Vector Related Functions
cross dist dist_sq distance dot length length_squared norm normalise normalize
vec2 vec3 vec4 vec5 vec6 tovec3 tovec4 tovec5 tovec6
rev rotl rotr
swap 12 swap 13 swap23 swapxy swapxz swapyz swaprg swaprb swapgb
neg1 neg2 neg3 neg12 negx negy negz negxy negr negg negb negrg
debump
Interpolation Functions
lerp mix scurveinterp scerp sininterp serp cubicinterp cerp cubic scurveinterpolant
scint sininterpolant sint
Step Functions
mapcui step smoothstep smoothsteplin smoothstepsc smoothstepsin smoothstepcs smoothstepsq smoothstepsqi smoothstepcb smoothstepcbi smoothstepsin smoothstepsini smoothstepcr smoothstepcri smoothsteprt smoothsteprti
stepn smoothstepn smoothstepnlin smoothstepnsc smoothstepncs smoothstepnsq smoothstepsqi smoothstepncb smoothstepncbi smoothstepnsini smoothstepncr smoothstepncri smoothstepncrt smoothstepnrti
Quantization
quantize quantizelin quantizesc quantizesin quantizecs
Oscillators
osc osci oscsc oscsin osccs osccubic oscsq oscsqi osccb osccbi oscpsin oscpsini osccr osccri oscrt oscrti
smoothosc smoothosclin smoothoscsin smoothosccs
osch oschi oschsc oschsin oschcs oschcubic oschsq oschsqi oschcb oschcbi oschpsin oschpsini oschcr oschcri oschrt oschrti
smoothosch smoothoschlin smoothoschsin smoothoschcs
Noise
noise noisei noisesc noisesin noisecs noisecubic noisesq noisesqi noisecb noisecbi noisepsin noisepsini noisecr noisecri noisert noiserti
noiseh noisehi noisehsc noisehsin noisehcs noisehcubic noisehsq noisehsqi noisehcb noisehcbi noisehpsin noisehpsini noisehcr noisehcri noisehrt noisehrti
perlin perlinsc perlinsin perlincubic perlincs
perlinh perlinhsc perlinhsin perlinhcubic perlinhcs
cellnoise cellnoise2 cellnoisedist cellnoiseedge
dir diri dirsc dirsin dircs
udiri udir udirsc udirsin udircs
dir3 dir3i dir3sc dir3sin dir3cs
udir3 udir3i udirsc udir3sin udir3cs
Other functions
var
Reserved/Internal Variable Names
w h x y rx ry R G B pi
Unknown: pc p pt px
Cannot be used as custom inputs: p p0 p1 p2 p3 p4 p5 (p followed by any number) however, these can be used as variable names.
Operators
+ - * / ^ ( ) . = ; ,
Exponentiation has the same priority as multiplication and division: a*b^c =(a*b)^c not a*(b^c).
I am making a series of video tutorials for Affinity Photo's procedural texture filter. This page provides an index for the functions used in each video.
var vec2 clampmin dot smoothstepn abs oschsin
var vec2 length
var vec2 perlincubic udirsc smoothstep norm debump dot pow
var vec2 clampmin clampmax osc smoothstep
var vec2 atan2 floor smoothstep length cos
var vec2 floor diri step length fraction
var fraction vec2 atan2 osci pow cos floor length
var fraction vec2 atan2 floor osc length pow cos
var vec2 atan2 floor osc length cos
var vec2 noiseh perlincubic
var vec2 vec3 sqrt fraction step max min smoothstep
var vec2 step perlincubic cellnoisedist
var vec2 fraction smoothstepcs length length max
var vec2 atan length sin smoothstep
var vec2 atan2 length cellnoise abs dirsc smoothstep
var vec2 cos sin vec3 oscsc osccri oschsc osci dirsc
rev rotl rotr swap12 swap13 swap23 swapxy swapxz swapyz swaprg swaprb swapgb neg1 neg2 neg3 neg12 negx negy negz negxy negr negg negb negrg
var norm vec2 sqrt swapxy fraction length step atan2 quantize
var vec2 pow noisei smoothstep osccr osci
var vec2 noisei cos sin smoothstep osc quantize perlinhcubic osc average
var normalize vec2 sqrt fraction step length smoothstep cos sin
var vec2 cos sin step osc smoothstep
var vec2 cellnoise cellnoise2 perlincubic perlinhcubic norm cos sin noisehcubic
var norm vec2 vec3 max oscsqi perlincubic noisehsc noiseh
var vec2 fraction length atan2 oscsc step
var norm vec2 cos sin perlincubic perlinhcubic noisehcubic
var vec2 cos sin normalize perlincubic noisehi
var vec2 cos sin oscsc
osc oscsc
var norm vec2 cellnoisedist dir noisehcubic perlinhcubic debump dot pow
var vec2 vec3 normalize floor fraction smoothstep noisei lerp cellnoise2 oscsc perlin perlincubic
var vec2 length
var vec2 noisei diri length atan udiri dir3i udir3i dirsc
var vec3 vec2 smoothstep perlincubic
var normalize vec2 fraction floor step
var normalize vec2 sqrt fraction step length smoothsteplin max abs dot swap12
var vec2oscsc osci max
var vec2 fraction oscrt osc fraction osch noisehsc max norm debump dot pow
var vec2 abs cellnoisedist perlinhcubic serp norm debump dot pow
var vec2 cellnoisedist quantize
var vec2 pow noisei smoothsteplin average cos sin osccr dirsc osc perlinhcubic
var vec2 vec3 perlinhcubic mapcui osccr step osc
var vec2 cellnoise saturate average
var vec2 perlinhcubic osch clamp
var vec2 mapcui max osccr perlinhcubic
Friday, June 14, 2019
Race Lights
/*
* File: RaceLightsMain.c
* Author: hmikelson
*
* Created on June 14, 2019, 1:26 PM
*/
#if defined(__XC)
#include <xc.h> /* XC8 General Include File */
#elif defined(HI_TECH_C)
#include <htc.h> /* HiTech General Include File */
#endif
#include <stdint.h> /* For uint8_t definition */
#include <stdbool.h> /* For true/false definition */
#include <stdlib.h> /*rand()*/
#pragma config MCLRE=OFF,CP=OFF,WDTE=OFF,FOSC=INTOSCIO
#define _XTAL_FREQ 4000000
uint8_t sGPIO;
void init()
{
//Configure GPIO Port
ANSEL = 0b00000000; //Configure all GPIO pins as digital
TRISIO = 0b11101000; //Set GP3 & 5 as input and the rest as outputs
OPTION_REGbits.nGPPU = 0;
WPU = 0b00100000; //Enable weak pullups on GP5
//Configuer AD Convertor
ADCON0 = 0x00; //AD disabled
ADRESH = 0x00; //Init the AD Register
//Configure Comparator
CMCON0 = 0xFF; // Comparator is turned off
CMCON1 = 0x00; // Comparator is turned off
//Interrupt configuration
INTCON = 0x00; //Disable all interrupts
}
void vdelay(int n)
{
int i;
for (i=0;i<=n;i++)
{
__delay_ms(150);
}
}
void main()
{
uint8_t r, db_cnt;
init();
GPIO = 0b00000001;
__delay_ms(100);
GPIO = 0b00000010;
__delay_ms(100);
GPIO = 0b00000100;
__delay_ms(100);
GPIO = 0b00000000;
while(1)
{
GPIO = 0b00000001;
__delay_ms(200);
for (db_cnt = 0; db_cnt <= 10; db_cnt++)
{
__delay_ms(10);
if (GPIObits.GP5 == 1)
db_cnt = 0;
}
GPIO = 0b00000010;
r = rand()%15;
__delay_ms(250);
vdelay(r);
__delay_ms(250);
GPIO = 0b00000001;
__delay_ms(50);
GPIO = 0b00000010;
__delay_ms(50);
GPIO = 0b00000100;
__delay_ms(50);
GPIO = 0b00000001;
__delay_ms(50);
GPIO = 0b00000010;
__delay_ms(50);
GPIO = 0b00000100;
__delay_ms(50);
GPIO = 0b00000100;
__delay_ms(200);
for (db_cnt = 0; db_cnt <= 10; db_cnt++)
{
__delay_ms(10);
if (GPIObits.GP5 == 1)
db_cnt = 0;
}
}
}
Sunday, March 3, 2019
Back Up Robot
/*
* File: BackupBotMain.c
* Author: hmikelson
*
* Created on January 12, 2019, 11:32 AM
*
* This program is used to control a robot with PIC16f1825 microcontroller
* Uses a customized circuit board with a 1 watt amplified speaker
* high intensity LED
* Ultrasound sensor, and PIR sensor
* Two motors: Forward, backward, right, left, stop
* 6V battery power
* Gear driven wheels
*/
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#pragma config MCLRE=OFF,CP=OFF,WDTE=OFF,FOSC=INTOSC
#define _XTAL_FREQ 16000000
unsigned char sPORTA=0b00000000, sPORTC=0b00000000, pwmax=63;
void init()
{
//Configure GPIO Port
//RA0=BI2,RA1=BI1,RA2=Stby,RC0=AI1,RC1=AI2,RC2=PWMA&B
//RC3=PIR,RC4=Trig,RC5=Echo,RA3=NA,RA4=Audio,RA5=LED
//ANSELC= 0b00000000; //Must disable analog inputs to read digital in
TRISA = 0b11000000; //O=all
TRISC = 0b11101000; //I=3,5
OSCCONbits.IRCF = 0b1111;
OSCCONbits.SCS = 0b00;
//Enable analog input on C3
ANSELCbits.ANSC3 = 1;
ADCON0 = 0b00011101; //Channel (CHS4:0 bits 6-2),ADON bit 1 enable Analog
ADCON1bits.ADCS = 0b111; // clock select dedicated osc
ADCON1bits.ADNREF = 0; //neg ref
ADCON1bits.ADPREF = 0; //config + voltage with vdd
ADCON1bits.ADFM = 1; //format left justified
ADRESH = 0x00; //Init the AD Register
}
void vdelay(int n)
{
int i;
for (i=0;i<=n;i++)
{
__delay_us(5);
}
}
// Read voltage from input
int read_v()
{
int val,v1,v2;
__delay_ms(1);
ADCON0bits.GO_nDONE = 1;
while(ADCON0bits.GO_nDONE == 1);
return (ADRESH<<8) + ADRESL;
}
/*
* Get distance from ultra sound sensor
* Sensor on pins C4 Trig, C5 Echo
*
*/
int getdist()
{
int i=0;
sPORTC = sPORTC & 0b11101111;
PORTC = sPORTC;
__delay_us(40);
sPORTC = sPORTC | 0b00010000;
PORTC = sPORTC;
__delay_us(10);
sPORTC = sPORTC & 0b11101111;
PORTC = sPORTC;
while((PORTCbits.RC5 == 0) && (i<5000)) //Wait but not forever
{
i++;
}
i=0;
while ((PORTCbits.RC5 == 1) && (i<1000)) //Wait but not forever
{
i++;
}
return i;
}
// Drive robot forward
void RobotFd(int time, unsigned char speed)
{
int i, j;
for (i=0;i<time;i++)
{
for (j=0;j<pwmax;j++)
{
if (j<speed)
{
PORTA = 0b00111110; // LED,Aud,MCLR,En,BI2,BI1
sPORTC=0b00000110 | sPORTC;
sPORTC=0b11111110 & sPORTC;
PORTC = sPORTC; // Eco,Trg,PIR,PWM,AI1,AI2
vdelay(pwmax/speed);
}
else
{
PORTA = 0b00000110;
sPORTC = 0b00000010 | sPORTC;
sPORTC = 0b11111010 & sPORTC;
PORTC = sPORTC;
vdelay(pwmax/speed);
}
}
}
}
// Move Robot backward
void RobotBk(int time, unsigned char speed)
{
int i, j;
for (i=0;i<time;i++)
{
for (j=0;j<pwmax;j++)
{
if (j<speed)
{
PORTA = 0b00111101;
sPORTC = 0b00000101 | sPORTC;
sPORTC = 0b11111101 & sPORTC;
PORTC = sPORTC;
vdelay(pwmax/speed);
}
else
{
PORTA = 0b00000101;
sPORTC = 0b00000001 | sPORTC;
sPORTC = 0b11111001 & sPORTC;
PORTC = sPORTC;
vdelay(pwmax/speed);
}
}
}
}
// Move the robot right for time at speed.
void RobotRt(int time, unsigned char speed)
{
int i, j;
for (i=0;i<time;i++)
{
for (j=0;j<pwmax;j++)
{
if (j<speed)
{
PORTA = 0b00111110;
sPORTC = 0b00000101 | sPORTC;
sPORTC = 0b11110101 & sPORTC;
PORTC = sPORTC;
vdelay(pwmax/speed);
}
else
{
PORTA = 0b00000110;
sPORTC = 0b00000001 | sPORTC;
sPORTC = 0b11111001 & sPORTC;
PORTC = sPORTC;
vdelay(pwmax/speed);
}
}
}
}
//Move the Robot Left for given time and speed.
void RobotLt(int time, unsigned char speed)
{
int i, j;
for (i=0;i<time;i++)
{
for (j=0;j<pwmax;j++)
{
if (j<speed)
{
PORTA = 0b00111101;
sPORTC = 0b00000110 | sPORTC;
sPORTC = 0b11111110 & sPORTC;
PORTC = sPORTC;
vdelay(pwmax/speed);
}
else
{
PORTA = 0b00000101;
sPORTC = 0b00000010 | sPORTC;
sPORTC = 0b11111010 & sPORTC;
PORTC = sPORTC;
vdelay(pwmax/speed);
}
}
}
}
// Robot stops for the given amount of time
void RobotSt(int time)
{
int i;
for (i=0;i<time;i++)
{
PORTA = 0b00000100;
sPORTC = 0b00000100 | sPORTC;
sPORTC = 0b11111100 & sPORTC;
PORTC = sPORTC;
__delay_ms(10);
}
}
void main()
{
int mt=100,dist=1000,i;
unsigned char rspeed, raction, rtime, nacts=30, pir;
init();
RobotFd(200,pwmax/1.5);
RobotRt(200,pwmax/1.5);
RobotBk(200,pwmax/1.5);
RobotLt(200,pwmax/1.5);
while(1)
{
while (nacts==0)
{
RobotSt(200);
pir = read_v();
if (pir>50)
{
nacts = 30;
}
}
dist = getdist();
if (dist<300)
{
RobotBk(mt,pwmax/2);
RobotRt(mt,pwmax/2);
}
else
{
RobotFd(mt,pwmax/2);
}
nacts = nacts - 1;
raction=(rand() % 5);
rtime=(rand() % mt)/2+mt/2;
rspeed=pwmax/((rand() % 15)+1)+pwmax/5;
switch (raction)
{
case 1:
RobotFd(rtime,rspeed);
break;
case 2:
RobotBk(rtime,rspeed);
break;
case 3:
RobotLt(rtime,rspeed);
break;
case 4:
RobotRt(rtime,rspeed);
break;
default:
RobotSt(rtime);
break;
}
}
}
Sunday, November 4, 2018
/*
* File: ls5main.c
* Author: Hans Mikelson
*
* Created on December 17, 2014, 8:59 AM
*/
#if defined(__XC)
#include <xc.h> /* XC8 General Include File */
#elif defined(HI_TECH_C)
#include <htc.h> /* HiTech General Include File */
#endif
#include <stdint.h> /* For uint8_t definition */
#include <stdbool.h> /* For true/false definition */
#include <stdlib.h> /* includes rand() */
#pragma config MCLRE=OFF,CP=OFF,WDTE=OFF,FOSC=INTOSCIO
#define _XTAL_FREQ 4000000
uint8_t sGPIO, gr1=0, gr2=0; // Global shadow register and random variables
void init()
{
//Configure GPIO Port
ANSEL = 0b00000000; //Configure all GPIO pins as digital
TRISIO = 0b110001000; //Set GP3 as input the rest as outputs
OPTION_REGbits.nGPPU = 0;
WPU = 0b00000000; //Disable weak pullups
//Configuer AD Convertor
ADCON0 = 0x00; //AD disabled
ADRESH = 0x00; //Init the AD Register
//Configure Comparator
CMCON0 = 0xFF; // Comparator is turned off
CMCON1 = 0x00; // Comparator is turned off
//Interrupt configuration
INTCON = 0x00; //Disable all interrupts
}
void vdelay(int n) //Variable time delay
{
int i;
for (i=0;i<=n;i++)
{
__delay_us(100);
}
}
void xfade(int n) //Cross fade between two global random #s
{ //n controls the very slow fade time
int j, k, i;
for (j=0;j<255;j++)
{
for (i=0;i<n;i++)
{
for (k=0;k<255;k++)
{
if (j<k)
{
GPIO=gr2;
}
else
{
GPIO=gr1;
}
}
}
}
gr2 = gr1;
gr1 = rand() & rand();
}
void strobe(int n) //Create a strobing cross fade
{
int j, k, i;
for (j=0;j<63;j++)
{
for (i=0;i<1;i++)
{
for (k=0;k<63;k++)
{
if (j<k)
{
GPIO=gr2;
vdelay(n);
}
else
{
GPIO=gr1;
vdelay(n);
}
}
}
}
gr2 = gr1;
gr1 = rand() & rand();
}
void main()
{
int i=1;
init();
GPIO = 0b00000000;
while(1)
{
if (GPIObits.GP3 == 0)
{
i=(i+1) % 9;
GPIO = 0b00000000;
vdelay(10000);
}
switch (i)
{
case 1: //Medium flash
GPIO = rand() & rand();
vdelay(4000);
break;
case 2: //Fast flash
GPIO = rand() & rand();
vdelay(2000);
break;
case 3:
xfade(1);
break;
case 4:
xfade(8);
break;
case 5:
xfade(32);
break;
case 6:
strobe(4);
break;
case 7:
strobe(8);
break;
case 8:
strobe(32);
break;
default: // Slow flash
GPIO = rand() & rand();
vdelay(8000);
break;
}
}
}