Hello folks!
This project distorts images by shifting pixels along the X and Y axes using sinusoidal wave functions. The result is a cool dynamic effect that moves through various patterns, from horizontal bands to diagonal shears, driven by the wave modulation.
That was fun!
Another cool thing you can do is call the event-driven functions in the code!
I used them at the start to trigger changes automatically, such as cycling through different distortion modes over time without needing any key presses.
All the coding was done by me!
AI was used for:
-
Scrutinizing and reviewing final code
It is asked NOT to provide code and only feedback!
I then scrutinize the feedback provided.
In the end this works if you are an experienced programmer. -
Commenting on the modes
-
It did not catch spelling errors! Maybe I forgot to ask that.
// Pixel Manipulation
// Author: glv
// Date: 2025-10-09
// Version: 1.2.0
// Shifts the color of pixels with sinusoidal modulation.
PImage img1, img2, img3;
// PGraphics pg; //Future!
int offset = 0; // Shifts left
void settings()
{
String name = "sunflower.jpg";
img1 = loadImage(name);
String urlString = "http://learningprocessing.com/code/assets/sunflower.jpg";
// Tests if file exists or requires downloading from URL
if (img1 != null)
{
println("File exists.");
}
else
{
println("File does not exist.");
img1 = loadImage(urlString);
img1.save("sunflower.jpg");
}
println(img1.width, img1.height);
println();
size(img1.width, img1.height, P2D);
//img2 = img1.get();
img2 = createImage(img1.width, img1.height, ARGB);
}
void setup()
{
}
void draw()
{
float den = 240;
if (frameCount < 8*den+1)
{
key = 'c';
if (frameCount == 1) keyPressed();
else if (frameCount == 1*den) keyPressed();
else if (frameCount == 2*den) keyPressed();
else if (frameCount == 3*den) keyPressed();
else if (frameCount == 4*den) keyPressed();
else if (frameCount == 5*den) keyPressed();
else if (frameCount == 6*den) keyPressed();
else if (frameCount == 7*den) keyPressed();
else if (frameCount == 8*den)
{
image(img1, 0, 0);
textAlign(CENTER, CENTER);
textSize(24);
text("That's all folks!", width/2, height/2);
noLoop();
return;
}
}
float angle = TAU/den;
float psx = (frameCount%den)*angle;
float psy = (frameCount%den)*angle;
//psx = 0;
int sliceWidth = 10;
imageMods3(sliceWidth, psx); //ok
image(img2, 0, 0);
//for (int y=0; y < img1.height; y+=sliceWidth)
// {
// stroke(128);
// strokeWeight(1);
// line(0, y, width, y);
// }
}
boolean togx = false;
boolean togy = true; //default
int shift, shiftx, shifty;
float modx, mody;
// Sine wave:
// y(t) = amp*sin(TAU*f*t + ps), f = 1/T, ps is phaseshift, TAU = 2*PI
int loc;
int loc2 = 0;
int x2, y2;
int choice = -1;
void imageMods3(int sliceWidth, float ps)
{
img2.loadPixels();
// outer y, inner x nested for loop
for (int y = 0; y < img2.height; y++ )
{
int amp = 20;
// y-axis mods here in outer y-loop to optimize; do not have to repeat in x loop
int sliceInty = (y/sliceWidth)*sliceWidth; // This is for quantization
//int sliceInty = y; or pass slicewidth 1
mody = amp*sin( sliceInty*(TAU/img2.height) + ps);
shifty = (int) mody; // use an int for quantization?
for (int x = 0; x < img2.width; x++)
{
if(togy)
loc = x + y*img2.width;
else
loc = y + x*img2.width;
// y-axis mods... are in outer loop
// x-axis mods
int sliceIntx = (x/sliceWidth)*sliceWidth; // This is for quantization
//int sliceIntx = x; // or pass slicewidth 1
modx = amp*sin( sliceIntx*(TAU/img2.width) + ps);
test(x, y, choice);
color c = img1.pixels[loc2];
img2.pixels[loc] = c;
}
}
img2.updatePixels();
}
void test(int x, int y, int choice)
{
shiftx = (int) modx; // use an int for quantization
shifty = (int) mody; // Same...
switch (choice)
{
case 0: // sine waves
int x2 = (x + shiftx + offset)%img2.width;
if (x2 < 0) x2 += img2.width;
loc2 = x2 + y*img2.width;
break;
case 1: // sine waves
x2 = (x + shifty + offset)%img2.width;
if (x2 < 0) x2 += img2.width;
loc2 = x2 + y*img2.width;
break;
case 2: // sine waves
y2 = (y + shifty + offset)%img2.height;
if (y2 < 0) y2 += img2.height;
loc2 = x + y2*img2.width;
case 3: // sine waves
y2 = (y + shiftx + offset)%img2.height;
if (y2 < 0) y2 += img2.height;
loc2 = x + y2*img2.width;
break;
case 4:
x2 = (x + shiftx + offset)%img2.width;
if (x2 < 0) x2 += img2.width;
y2 = (y + shifty + offset)%img2.height;
if (y2 < 0) y2 += img2.height;
loc2 = x2 + y2*img2.width;
break;
case 5:
x2 = (x + shiftx + offset)%img2.width;
if (x2 < 0) x2 += img2.width;
y2 = (y + shifty + offset)%img2.height;
if (y2 < 0) y2 += img2.height;
loc2 = y2 + x2*img2.width;
break;
case 6:
x2 = (x + shiftx + offset)%img2.width;
if (x2 < 0) x2 += img2.width;
y2 = (y + shiftx + offset)%img2.height; // Note: shiftx used for y2
if (y2 < 0) y2 += img2.height;
loc2 = x2 + y2*img2.width;
break;
case 7:
x2 = (x + shifty + offset) % img2.width; // Note: shifty used for x2
if (x2 < 0) x2 += img2.width;
y2 = (y + shifty + offset) % img2.height;
if (y2 < 0) y2 += img2.height;
loc2 = x2 + y2 * img2.width;
break;
}
}
void keyPressed()
{
if (key == 'l')
togy = !togy;
if (key == 'c')
{
choice = (choice+1)%8;
//println(choice);
println("Google Gemini description:");
println(modeNames1[choice]); // modeNames1 or modeNames2
println();
println("ChatGPT description:");
println(modeNames2[choice]);
println();
}
if (key == ' ')
{
frameCount = -1;
choice = -1;
}
}
// Google Gemini mode descriptions.
// I then asked for an array if Strings with these to integrate with code.
// Subject to scrutiny!
// Add this array outside of any function, near your other global variables:
String[] modeNames1 = {
"Mode 0: Columnar Horizontal. Source: X-Wave (modx). Shift: X-Axis only.",
"Mode 1: Banded Horizontal. Source: Y-Wave (mody). Shift: X-Axis only (Cross-Axis).",
"Mode 2: Banded Vertical. Source: Y-Wave (mody). Shift: Y-Axis only.",
"Mode 3: Columnar Vertical. Source: X-Wave (modx). Shift: Y-Axis only (Cross-Axis).",
"Mode 4: Standard Diagonal. Source: X & Y Waves. Shift: Both X and Y Axes.",
"Mode 5: Swapped Diagonal. Source: X & Y Waves. Shift: Both X & Y Axes, Swapped Indexing.",
"Mode 6: Columnar Shear. Source: X-Wave (modx). Shift: Both X and Y Axes (Co-Modulated).",
"Mode 7: Banded Shear. Source: Y-Wave (mody). Shift: Both X and Y Axes (Co-Modulated)."
};
// Chat GPT mode descriptions
// Subject to scrutiny!
String[] modeNames2 = {
// Mode 0: Columnar Horizontal
// Shifts pixels horizontally based on the sine wave modulation in the X-direction (modx).
// Only the X-axis is affected, creating a horizontal column-like distortion.
"Mode 0: Columnar Horizontal. Shifts pixels horizontally based on the X-wave (modx). Affects only the X-axis, creating a columnar distortion across the image.",
// Mode 1: Banded Horizontal
// Shifts pixels horizontally based on the sine wave modulation in the Y-direction (mody).
// This mode affects the X-axis while creating horizontal bands that vary in intensity.
"Mode 1: Banded Horizontal. Shifts pixels horizontally based on the Y-wave (mody). Affects only the X-axis, creating horizontal bands of distortion.",
// Mode 2: Banded Vertical
// Shifts pixels vertically based on the sine wave modulation in the Y-direction (mody).
// This mode creates vertical bands, as only the Y-axis is affected.
"Mode 2: Banded Vertical. Shifts pixels vertically based on the Y-wave (mody). Affects only the Y-axis, creating vertical bands with varying displacement.",
// Mode 3: Columnar Vertical
// Shifts pixels vertically based on the sine wave modulation in the X-direction (modx).
// Only the Y-axis is affected, creating a column-like distortion along the Y-axis.
"Mode 3: Columnar Vertical. Shifts pixels vertically based on the X-wave (modx). Affects only the Y-axis, resulting in vertical columnar distortion.",
// Mode 4: Standard Diagonal
// Shifts pixels in both the X and Y axes using the X-wave (modx) and Y-wave (mody).
// This creates a typical diagonal shift with varying displacement in both axes.
"Mode 4: Standard Diagonal. Shifts pixels in both the X and Y axes based on X-wave (modx) and Y-wave (mody), creating a diagonal distortion effect.",
// Mode 5: Swapped Diagonal
// Similar to Mode 4, but the X and Y modulations are swapped, affecting the image in a reversed diagonal pattern.
// This results in a more complex diagonal distortion with swapped index usage.
"Mode 5: Swapped Diagonal. Shifts pixels in both the X and Y axes, but swaps the X and Y index calculations, creating a swapped diagonal distortion.",
// Mode 6: Columnar Shear
// Shifts pixels in both X and Y axes based on the sine wave modulation in the X-direction (modx).
// This mode introduces a shear effect, distorting the image in both axes with X-based modulation.
"Mode 6: Columnar Shear. Shifts pixels in both the X and Y axes based on the X-wave (modx), creating a shear-like distortion across the image.",
// Mode 7: Banded Shear
// Similar to Mode 6, but the Y-wave (mody) is used for modulation instead of the X-wave (modx).
// This creates a shear distortion with horizontal and vertical banding effects.
"Mode 7: Banded Shear. Shifts pixels in both the X and Y axes based on the Y-wave (mody), creating a shear effect with banding."
};
After posting some screen grabs I now want to automate and save frames in code!
Another day… :)
Have fun!
Reference:
Images and Pixels / Processing.org
:)