Coding like it’s 2023
There have been a lot of oohs and aahs regarding chatGPT’s ability to generate code recently.
The feeling I get is that there is something profound happening here. At the same time, the experience is a bit like trying to have a rational discussion inside a dream. Coherence is not guaranteed :)
From time to time you will get compilation errors, but if you point out the error - ChatGPT will politely apologize and then suggest a way to correct the error.
I have tested several approaches to code generation. I first tried to feed it a rather complex description of an inverse kinematics problem for an 18 DOF hexapod robot. That didn’t turn out too well.
I then started with a simplified description of the same problem and then iteratively refined the code by adding features and constraints to the existing code base. That produced rather impressive results. I haven’t been able to verify the correctness of this code, but even if the output is flawed, I suspect that it is a decent starting point for an implementation. I also suspect that it might work as an excellent creative trigger to get you over the initial “bump” when trying to solve a problem in a new domain.
The following is a transcript of the large language model-assisted coding session for a simpler, but more open-ended problem.
I decided to let it create a simple demo application in Processing.
The Conversation⌗
float angle = 0;
void setup() {
size(500, 500);
background(0);
colorMode(HSB, 360, 100, 100);
smooth();
}
void draw() {
translate(width/2, height/2);
rotate(angle);
stroke(angle%360, 100, 100);
line(0, 0, 50, 50);
angle += 0.1;
}
float angle = 0;
float radius = 50;
int numLines = 100;
int numColors = 8;
color[] colors = new color[numColors];
float[] colorOffsets = new float[numColors];
void setup() {
size(500, 500);
background(0);
colorMode(HSB, 360, 100, 100);
smooth();
// Define colors and offsets
for (int i = 0; i < numColors; i++) {
colors[i] = color(i*360/numColors, 100, 100);
colorOffsets[i] = random(0, 2*PI);
}
}
void draw() {
background(0);
translate(width/2, height/2);
rotate(angle);
for (int i = 0; i < numLines; i++) {
float t = i*2*PI/numLines;
float x = radius*cos(t);
float y = radius*sin(t);
float d = sqrt(x*x + y*y);
float a = atan2(y, x);
float c = colors[int((a+angle)*numColors/(2*PI))%numColors];
stroke(c);
strokeWeight(map(d, 0, radius, 2, 0));
line(x, y, 0, 0);
}
angle += 0.01;
}
float increment = 0.01;
float zOff = 0;
void setup() {
size(500, 500);
background(0);
colorMode(HSB, 360, 100, 100);
smooth();
}
void draw() {
loadPixels();
float yOff = 0;
for (int y = 0; y < height; y++) {
float xOff = 0;
for (int x = 0; x < width; x++) {
float noiseVal = noise(xOff, yOff, zOff);
int bright = int(map(noiseVal, 0, 1, 0, 100));
color c = color(255);
if (bright < 50) {
c = color(0, 0, bright*2);
}
pixels[x + y*width] = c;
xOff += increment;
}
yOff += increment;
}
updatePixels();
zOff += increment/10;
}
ArrayList<Particle> particles;
int numParticles = 200;
float spinSpeed = 0.01;
float particleSpeed = 0.5;
float particleSize = 4;
float increment = 0.01;
float zOff = 0;
PImage toasterImg;
float toasterX = 0;
float toasterY = 0;
float toasterSpeed = 2;
float toasterSize = 60;
void setup() {
size(500, 500);
colorMode(HSB, 360, 100, 100);
smooth();
particles = new ArrayList<Particle>();
for (int i = 0; i < numParticles; i++) {
particles.add(new Particle());
}
toasterImg = loadImage("toaster.png");
}
void draw() {
loadPixels();
float yOff = 0;
for (int y = 0; y < height; y++) {
float xOff = 0;
for (int x = 0; x < width; x++) {
float noiseVal = noise(xOff, yOff, zOff);
int bright = int(map(noiseVal, 0, 1, 0, 100));
color c = color(255);
if (bright < 50) {
c = color(0, 0, bright*2);
}
pixels[x + y*width] = c;
xOff += increment;
}
yOff += increment;
}
updatePixels();
translate(width/2, height/2);
rotate(frameCount * spinSpeed);
for (Particle p : particles) {
p.update();
p.display();
}
imageMode(CENTER);
image(toasterImg, toasterX, toasterY, toasterSize, toasterSize);
if (toasterX < -width/2-toasterSize/2) {
toasterX = width/2+toasterSize/2;
toasterY = random(-height/2+toasterSize/2, height/2-toasterSize/2);
}
toasterX -= toasterSpeed;
zOff += increment/10;
}
class Particle {
PVector pos;
PVector vel;
color c;
Particle() {
float angle = random(0, 2*PI);
float radius = random(50, 200);
pos = new PVector(radius*cos(angle), radius*sin(angle));
vel = pos.copy().rotate(PI/2).setMag(particleSpeed);
c = color(random(0, 360), 100, 100);
}
void update() {
pos.add(vel);
if (pos.mag() < particleSize) {
pos.setMag(particleSize);
vel.rotate(PI);
}
if (pos.mag() > 200) {
pos.setMag(200);
vel.rotate(PI);
}
}
void display() {
noStroke();
fill(c);
ellipse(pos.x, pos.y, particleSize, particleSize);
}
}
(The code doesn’t run because it refers to a nonexisting file “toaster.png”)
I then prompted DALLE with “A 3D rendered image of a chrome toaster with angel wings. High quality. Transparent background.” and made the background transparent in Photoshop.
The previous code now runs fine :)
ArrayList<Particle> particles;
int numParticles = 200;
float spinSpeed = 0.01;
float particleSpeed = 0.5;
float particleSize = 4;
float increment = 0.01;
float zOff = 0;
PImage toasterImg;
ArrayList<Toaster> toasters;
int numToasters = 10;
void setup() {
size(500, 500);
colorMode(HSB, 360, 100, 100);
smooth();
particles = new ArrayList<Particle>();
for (int i = 0; i < numParticles; i++) {
particles.add(new Particle());
}
toasterImg = loadImage("toaster.png");
toasters = new ArrayList<Toaster>();
for (int i = 0; i < numToasters; i++) {
toasters.add(new Toaster());
}
}
void draw() {
loadPixels();
float yOff = 0;
for (int y = 0; y < height; y++) {
float xOff = 0;
for (int x = 0; x < width; x++) {
float noiseVal = noise(xOff, yOff, zOff);
int bright = int(map(noiseVal, 0, 1, 0, 100));
color c = color(255);
if (bright < 50) {
c = color(0, 0, bright*2);
}
pixels[x + y*width] = c;
xOff += increment;
}
yOff += increment;
}
updatePixels();
translate(width/2, height/2);
rotate(frameCount * spinSpeed);
for (Particle p : particles) {
p.update();
p.display();
}
for (Toaster t : toasters) {
t.update();
t.display();
}
zOff += increment/10;
}
class Particle {
PVector pos;
PVector vel;
color c;
Particle() {
float angle = random(0, 2*PI);
float radius = random(50, 200);
pos = new PVector(radius*cos(angle), radius*sin(angle));
vel = pos.copy().rotate(PI/2).setMag(particleSpeed);
c = color(random(0, 360), 100, 100);
}
void update() {
pos.add(vel);
if (pos.mag() < particleSize) {
pos.setMag(particleSize);
vel.rotate(PI);
}
if (pos.mag() > 200) {
pos.setMag(200);
vel.rotate(PI);
}
}
void display() {
noStroke();
fill(c);
ellipse(pos.x, pos.y, particleSize, particleSize);
}
}
class Toaster {
PVector pos;
float speed;
float size;
Toaster() {
pos = new PVector(random(-width/2, width/2), random(-height/2, height/2));
speed = random(1, 4);
size = random(20, 80);
}
void update() {
pos.x -= speed;
if (pos.x < -width/2-size/2) {
pos.x = width/2+size/2;
pos.y = random(-height/2+size/2, height/2-size/2);
}
}
void display() {
image(toasterImg, pos.x, pos.y, size, size/2);
}
}
PImage toasterImg;
ArrayList<FlyingToaster> toasters;
float teapotAngle;
void setup() {
size(800, 600);
toasterImg = loadImage("toaster.png");
toasters = new ArrayList<FlyingToaster>();
for (int i = 0; i < 10; i++) {
toasters.add(new FlyingToaster(random(0, width), random(0, height), random(-5, 5), random(-5, 5)));
}
}
void draw() {
background(0);
translate(width/2, height/2);
rotate(teapotAngle);
teapotAngle += 0.01;
fill(255, 0, 0);
stroke(255);
strokeWeight(2);
rect(-50, -50, 100, 100);
noFill();
strokeWeight(1);
ellipse(0, 0, 150, 150);
for (FlyingToaster t : toasters) {
t.move();
t.checkEdges();
t.display();
}
}
class FlyingToaster {
PVector pos, vel;
float size, spin;
FlyingToaster(float x, float y, float vx, float vy) {
pos = new PVector(x, y);
vel = new PVector(vx, vy);
size = random(40, 60);
spin = random(-0.1, 0.1);
}
void move() {
pos.add(vel);
spin += random(-0.01, 0.01);
}
void checkEdges() {
if (pos.x < 0 || pos.x > width) {
vel.x *= -1;
}
if (pos.y < 0 || pos.y > height) {
vel.y *= -1;
}
}
void display() {
pushMatrix();
translate(pos.x, pos.y);
rotate(spin);
image(toasterImg, 0, 0, size, size/2);
popMatrix();
}
}
PImage toasterImg;
ArrayList<FlyingToaster> toasters;
float teapotAngle;
void setup() {
size(800, 600);
toasterImg = loadImage("toaster.png");
toasters = new ArrayList<FlyingToaster>();
for (int i = 0; i < 10; i++) {
toasters.add(new FlyingToaster(random(0, width), random(0, height), random(-5, 5), random(-5, 5)));
}
}
void draw() {
background(0);
translate(width/2, height/2);
rotate(teapotAngle);
teapotAngle += 0.01;
fill(255, 0, 0);
stroke(255);
strokeWeight(2);
rect(-50, -50, 100, 100);
strokeWeight(3);
noFill();
ellipse(0, 0, 150, 150);
strokeWeight(2);
beginShape();
vertex(0, -40);
vertex(40, 40);
vertex(-40, 40);
endShape(CLOSE);
for (FlyingToaster t : toasters) {
t.move();
t.checkEdges();
t.display();
}
fill(0, 0, 255);
noStroke();
rect(-100, height/4, 50, 50);
rect(-100, -height/4 - 50, 50, 50);
rect(50, height/4, 50, 50);
rect(50, -height/4 - 50, 50, 50);
}
class FlyingToaster {
PVector pos, vel;
float size, spin;
FlyingToaster(float x, float y, float vx, float vy) {
pos = new PVector(x, y);
vel = new PVector(vx, vy);
size = random(40, 60);
spin = random(-0.1, 0.1);
}
void move() {
pos.add(vel);
spin += random(-0.01, 0.01);
}
void checkEdges() {
if (pos.x < 0 || pos.x > width) {
vel.x *= -1;
}
if (pos.y < 0 || pos.y > height) {
vel.y *= -1;
}
}
void display() {
pushMatrix();
translate(pos.x, pos.y);
rotate(spin);
image(toasterImg, 0, 0, size, size/2);
popMatrix();
}
}
I may be anthropomorphizing a bit, but from this point on, I sense a slightly passive-aggressive vibe coming from ChatGPT. Ref “Feel free to customize it further to your liking”. It seems to have lost its context, and any further attempts at refining the code result in a total deterioration of the code and also incomplete code. Even the old trick “please show me the rest of the code without repeating what you just showed me” doesn’t seem to work anymore.
The code is incomplete and any attempts to force it to complete the code ends up with it starting from scratch. It does manage to provide some decent quotes, though :)
text("Code with Cowbell!", width/2, height/2-120);
textSize(40);
text("Coding is music to my ears!", width/2, height/2-50);
textSize(30);
text("You're the reason our world is so dynamic!", width/2, height/2+20);
textSize(20);
text("Keep on coding and never stop learning!", width/2, height/2+70);
The way that the session ended was almost poetic.