Maker

A Progressive Journey Through Working With AI Art – Part 6 – AI Is Boring

A few months ago I started a sort of series on going through using Stable Diffusion and AI Art.  I had some ideas for some more parts to this series, specifically on “Bad Results” and another possibly going into Text based AI.  I never got around to them.  Maybe I’ll touch on them a bit here.  The real point I want to make here…

I kind of find AI to be boring.

That’s the gist of it.  On the surface, it’s a really neat and interesting concept.  Maybe over time it gets better and actually becomes interesting.  But as it is now, I find it’s pretty boring and a little lame.  I know this is really contradictory to all the hype right now, though some of that ype may be dying a bit as well.  I barely see anything about AI art, it’s all “ChatGPT” now, and even that seems like it’s waiting a bit in popularity as people accept that it’s just, “Spicy Autocomplete”.

Maybe it’s just me though, maybe I’m missing some of the coverage because I am apathetic to the state of AI.  I also don’t think it’s going to be the end all be all creativity killer.  It’s just, not that creative.  It’s also incredibly unfulfilling, at least as a person who ostensibly is a “creator”.  I am sure boring bean counter types are frothing at the idea of an AI to generate all their logos or ad copy or stories so they can fire more people and suck in more money not having to pay people.  That’s a problem that’s probably going to get worse over time.  But the quality will drop.

But why is it boring?

Let’s look at the actual process, I’ll start with the image aspect, since I’ve used it the most.  You make a prompt, maybe you cut and paste in some modifier text to make sure it comes out not looking like a grotesque life-like Picasso, then you hit “generate”.  If you’re using an online service, you probably get like, 20-25 of these generations a month for free, or you get to pay some sort of subscription for more.  If you are doing it locally, you can just do it all you want.  And you’re going to need to do it a lot.  For every perfect and gorgeous looking image you get, you’re probably getting 20 really mediocre and weird looking images.  The subject won’t be looking the right direction, they will have extra limbs, or lack some limbs, the symmetry will be really goofy.  Any number of issues.  Often it’s just, a weird blob somewhere in the middle that feels like it didn’t fill in.  Also often, with people, the proportions will be all jacked up.  Weird sized head, arms or legs that are not quite the right length.

You get the idea.  This touches on the “bad results” I mentioned above.  Stable Diffusion is great at generating bad results.

It also, is really really bad at nuance.    The more nuance you need or want, the less likely you will get something useful.  Because it’s not actually “intelligent”.  It’s just, “making guesses”.  

You do a prompt for “The Joker”, you will probably get a random image of the Batman villain.  “The Joker, standing in a warehouse” might work after 3 or 4 tries, though it probably will give you plenty of images that are not quite “a warehouse.”  

But you want say, “The Joker, cackling madly while being strangled by Batman in a burning warehouse while holding the detonator for a bomb.”  You aren’t going to get jack shit.  that’s just, too much for AI to comprehend.  It fails really badly anytime you have multiple subjects, though sometimes you can get side by sides.  It fails even ore when those two people are interacting, or each doing individual things.  If you are really skilled you can do in painting and lots of generation to maybe get the above image, but at that point, you may as well just, draw it yourself.  Because it would probably take less time.  

In the end, as I mentioned, it’s also just, unfulfilling.  Maybe you spend all day playing with prompts and in painting and manage to get your Joker and Batman fighting image.  And so what?  You didn’t draw it, you didn’t create it, you pay as well have done a Google Image search or flipped through Batman comics to find a panel that matches this description.  You didn’t create anything, you hit refresh on a webpage for hours.

Even just, manually Photoshopping some other images together would be more fulfilling of an experience.  And the result will probably be better, since AI likes to give all these little tells.

Then there is text and ChatGPT.  I admit, I have not used it quite as much, but it seems to be mostly good at producing truthy Wiki-style articles.  It’s just the next generation Alexa/Siri at best.  It’s also really formulaic in it’s results.  It’s very, “this is a 5th grade report” in it’s structure for anything it writes.  Intro, three descriptive paragraphs, an outro restating your intro.  

Given how shit the education system is anymore, I guess it’s not that surprising this feels impressive.

Another issue is that it’s so sterile in it’s responses.  There were some things going around about how to “hack” it into making it say dirty things, but most of that has been ironed out.  I mean, I don’t NEED my AI chat bot to swear, or say mean things, but it goes to such extremes to avoid this that it kind of sucks the fun out of things.  For example, i asked it “Who would win in a cage match battle to the death between Siri, Cortana, and Alexa” and it tells me it can’t answer that because it’s violent.

It’s AI constructs.  I just want to get a stupid silly response.  Please pretend that you’re talking about fictional entities having a fictional fight.  Meanwhile, on this site Character.ai, which lets you create AI bots with personalities of people, real and fake, I have antagonized a Tom Riddle bot into trying to kill me, repeatedly.

It’s funny as shit.  It’s way more compelling and fun than WikipediaBotGPT.

I also tried to make some code with ChatGPT.  It does, ok-ish, with popular languages like Java, HTML, Python etc.  I asked it to make some LSL (Linden Scripting Language) code and it made something that follows the proper structure, but does nothing.  I got similar results trying to get it to create Pong in NES Assembly that I could compile into a ROM.  I had a friend look over the code and help me follow it and it basically gave the 2nd paddle nothing to do.  It also didn’t seem to actually draw out any graphics anywhere.  Which is another issue with the code part.  It can produce single file responses.  It’s not going to produce base code, and some sprites to go with it, and maybe some imported files that do other things.  

(The code below does not do anything)

; Pong game for NES
; Written in 6502 Assembly

; Constants
P1_PADDLE_Y = $20  ; P1 paddle vertical position
P2_PADDLE_Y = $80  ; P2 paddle vertical position
BALL_RADIUS = $03  ; Ball radius

; Variables
P1_SCORE = $00      ; P1 score
P2_SCORE = $00      ; P2 score
BALL_X = $80        ; Ball horizontal position
BALL_Y = $60        ; Ball vertical position
BALL_DX = $01       ; Ball horizontal velocity
BALL_DY = $01       ; Ball vertical velocity

; Main loop
MAIN:
    ; Read input
    LDA $4016   ; Read controller 1 input
    AND #%00000001  ; Mask A button
    BEQ .P1_DOWN    ; If A button not pressed, go to P1_DOWN
    INC P1_PADDLE_Y ; If A button pressed, move P1 paddle up
    JMP .UPDATE_P1_PADDLE ; Go to UPDATE_P1_PADDLE
.P1_DOWN:
    LDA $4016   ; Read controller 1 input
    AND #%00000010  ; Mask B button
    BEQ .P1_STILL   ; If B button not pressed, go to P1_STILL
    DEC P1_PADDLE_Y ; If B button pressed, move P1 paddle down
    JMP .UPDATE_P1_PADDLE ; Go to UPDATE_P1_PADDLE
.P1_STILL:
    ; Do nothing

.UPDATE_P1_PADDLE:
    ; Update P1 paddle position
    STA $2003  ; Set PPU address to P1 paddle Y position
    LDA P1_PADDLE_Y
    STA $2004
    JMP .UPDATE_BALL  ; Go to UPDATE_BALL

.UPDATE_P2_PADDLE:
    ; Update P2 paddle position
    STA $2003  ; Set PPU address to P2 paddle Y position
    LDA P2_PADDLE_Y
    STA $2004
    JMP .UPDATE_BALL  ; Go to UPDATE_BALL

.UPDATE_BALL:
    ; Update ball position
    CLC
    LDA BALL_X
    ADC BALL_DX
    STA BALL_X
    LDA BALL_Y
    ADC BALL_DY
    STA BALL_Y

    ; Check for collision with top or bottom wall
    LDA BALL_Y
    CMP #%00000011
    BCC .NO_COLLISION_TOP
    SEC
    SBC #%00000100
    STA BALL_Y
    LDA BALL_DY
    EOR #%11111111
    INC BALL_DY
    EOR #%11111111
.NO_COLLISION_TOP:
    LDA BALL_Y
    CMP #%11111100
    BCS .NO_COLLISION_BOTTOM
    SEC
    ADC #%00000100
    STA BALL_Y
    LDA BALL_DY
    EOR #%11111111
    INC BALL_DY
    EOR #%11111111
.NO_COLLISION_BOTTOM:

    ; Check for collision with P1 paddle
    LDA BALL_X
    CMP #%00000100
    BCC .NO_COLLISION_P1
    LDA BALL_X
    CMP #%00000100+BALL_RADIUS
    BCS .NO_COLLISION_P1
    LDA BALL_Y
    CMP P1_PADDLE_Y
    BCC .

Like generating the Joker/Batman image, it’s just not that smart.  It’s auto-completing a response based on probabilities.  It doesn’t understand how to actually break down code into parts, or what other files may be needed to make the code work.

A lot of the problem in general I think, is the more you use these tools, the more the trick becomes glaringly obvious.  The repetition in results, both images and text, really how how completely unintelligent, the “Artificial intelligence” is.  It’s just regurgitating the same things, over and over, with slightly different phrasings.

Ending My Experiment with Flask

I really like Python, but I have one major gripe with it. It’s a pain in the ass to actually share any projects. Python can’t easily be packaged into an executable, and it doesn’t really work well on the web. I had some vague high hopes of building some Flask based web apps, and I may revisit it at some point, but even just trying to share some of the projects I made during my Python class had become a cumbersome pain to manage.

I had originally set up the blog project over on JoshMiller.net. A domain I have, and don’t really want to dump, but don’t really have a use for. I also do not need another blog. “Traditionally” I would use that domain for “personal blogging” but that’s what this site is for, and I’m happy with the state of things here. The Flask blog was also extremely basic and would require a lot of code work to make workable, with things like image management and an RSS feed and some sort of theme system etc. I could probably do it, but…. why?

One thing I’ve learned after working on many fruitless projects over the years. It’s ok for a project to be kind of pointless, but it’s not ok for it to just be completely pointless. There are plenty of much better platforms for blogging, and frankly, all of them are “Just use WordPress”. Another angle is learning how to run sites with something besides Apache, but that tine would be better just learning Nginx.

Anyway, part of the point was to take the base Flask Blog app, and integrate other Flash Apps into it. The problem is, all of the ones I have built are designed to be stand alone apps. They often reuse some name space so i can’t just copy/paste the code in. Then things start to get screwy with modules and imports. The code itself is also not super reusable. Flask can host HTML code, but it’s all very specifically laid out and structured. I can’t just drop the same code into a folder somewhere and run it on a straight Apache instance.

What does work well for this, is JavaScript. Which is basically entirely intended for web based use. Most of the ideas I have had for projects would be much better suited running as JavaScript sites that are much more portable.

Anyway, all of this is mostly to say, I have disabled the Flask Blog I had hosted on JoshMiller.net and rolled it back to the broken landing page I was building. I don’t really care about this site anyway. Hell even the landing page concept is redundant because I’ve got a nice little Github Page serving that purpose.

Code Project – JavaScript Drum Kit

The second actual project from the class I’ve been doing is a simple clickable drum kit. Part of the purpose was to work with signs and to work with clickable elements. Like the dice roller game, the basic HTML and images were provided, though there was some freedom in arranging the icons and sounds.

It feel a bit flaky, when it comes to overlapping sounds, but that also feels like a bit of a limitation with the code itself, or at least, the simplicity of what is being done here. I’m sure it’s “fixable” with some more advanced work. But for now it’s pretty neat. Works with clicks and keyboard presses.

It can be played by going here, and the full repository can be found here.

var w = new Audio('sounds/snare.mp3');
var a = new Audio('sounds/tom-1.mp3');
var s = new Audio('sounds/tom-3.mp3');
var d = new Audio('sounds/kick-bass.mp3');
var j = new Audio('sounds/tom-4.mp3');
var k = new Audio('sounds/tom-2.mp3');
var l = new Audio('sounds/crash.mp3');

for(var i=0 ; i < document.querySelectorAll(".drum").length; i++) {
    document.querySelectorAll(".drum")[i].addEventListener("click", function(event) {
    clickHandler(event.target.innerHTML)});
}

document.addEventListener("keydown", function(event) {
    clickHandler(event.key);
});

function clickHandler(which_sound) {
//    console.log(which_sound)
//    alert("Clicked!"); 
    flashButton(which_sound);

    switch (which_sound){
    case "w":
        w.play();
        break;
    case "a":
        a.play();
        break;
    case "s":
        s.play();
        break;
    case "d":
        d.play();
        break;
    case "j":
        k.play();
        break;
    case "k":
        k.play();
        break;
    case "l":
        l.play();
        break;
    default:
        console.log(which_sound)
        break;
    }
}

function flashButton (whichFlash) {
    var activeButton = document.querySelector("."+whichFlash);
    activeButton.classList.add("pressed");
    setTimeout(function(){ activeButton.classList.remove("pressed"); },0.3);
}

The large bulk of the code here is handling the sound. I feel like there could almost be a more elegant way of handling this process with a list, but I’m not positive since it would mean taking the text of a list element and using it as the name of a variable. The top chunk of definitions manages creating the sound elements themselves, and the switch statement in the middle plays one based on what the user did.

This function:

function flashButton (whichFlash) {
    var activeButton = document.querySelector("."+whichFlash);
    activeButton.classList.add("pressed");
    setTimeout(function(){ activeButton.classList.remove("pressed"); },0.3);
}

Makes the flash happen. Like I did for the Python class, I tried to construct most of these on my own, so had to do a bit of research to find a way to make the button add the “pressed” class, to get the bright effect, then shortly after, remove it. This seemed to be the appropriate method based on a couple of sources.

The real “meat” of this lesson was this bit of code.

for(var i=0 ; i < document.querySelectorAll(".drum").length; i++) {
    document.querySelectorAll(".drum")[i].addEventListener("click", function(event) {
    clickHandler(event.target.innerHTML)});
}

document.addEventListener("keydown", function(event) {
    clickHandler(event.key);
});

Specifically, using the event listener function, to call an embedded function in order to pass data around. Its similar in concept to some things I’d done in Python, and I am sure it’s nothing super fancy in technique, but its “new to me” in the Javascript sense. Also, I think the instructor handled this a different way, but I’ve set these two event listeners, one for the buttons for clicks, and one for the page for key presses, in a way that made the clickHandler a lot simpler, since they both work off of the same bit of data.

Code Project – JavaScript Dice Roller Game

I mentioned in my Python Class posts that I really enjoyed the instructor and I planned to eventually move to doing her Web Dev Course. Well, I’ve started working on that one. It’s not a “100 Days of Projects” so I’ve not been quite blitzing through it like I did the Python Course, and it’s a lot of material I am already way more familiar with. Hell, I skipped over the first third of the course because it was the same HTML/CSS content included int he Python Course.

My main goal here, is to get much more familiar with JavaScript. I have dabbled, lightly, in JavaScript (ok, let’s go with JS from here on), and well, it’s a coding language, so I already have a lot of the “logic” side down. At some point, learning new programming languages just becomes about learning the syntax and maybe some of the special uses for a particular language. I’d only say I am Intermediate or above in Python and HTML/PHP, but I’m familiar with several other languages including BASIC, C, C++, LSL (Linden Scripting Language), Arduino (mostly just C), Bash Shell scripting, SQL, Cold Fusion, and probably some other I am forgetting.

It all mostly works the same on the base level, a loop is a loop, conditionals are conditional, and it’s just a matter of remembering if the language uses braces or parenthesis or semi colons and if tabs matter. And that’s where Google becomes your friend.

Anyway, the first real stand along project of this course is a simple Dice rolling game. And I have to say, “game” is the loosest sense of the word. It’s a webpage you refresh to see if Player 1 or Player 2 has the higher dice face. I might actually try to make a more complex version of this later where you select from different Dice and it rolls them, for you know, gaming. Sure there are plenty of D&D Dice roller apps, but it would be a fun exercise.

The class provided the basic HTML Structure and graphics, since the focus was on the JavaScript side of things. It’s not an overly complex project, but here is the JS Code.

function setImage(diceImage, roll) {
    console.log(roll);
    if(roll === 1) { rolled = "images/dice1.png"; }
    else if(roll === 2) { rolled = "images/dice2.png"; }
    else if(roll === 3) { rolled = "images/dice3.png"; }
    else if(roll === 4) { rolled = "images/dice4.png"; }
    else if(roll === 5) { rolled = "images/dice5.png"; }
    else { rolled = "images/dice6.png"; }


    diceImage.src = rolled;
}

let player1Roll = Math.floor(Math.random()*6)+1;
let player2Roll = Math.floor(Math.random()*6)+1;

let dice1Image = document.querySelector("img.img1");
let dice2Image = document.querySelector("img.img2");

if( player1Roll > player2Roll) {
    document.getElementById("headtext").textContent = "Player 1 Wins!";
}
else if (player2Roll > player1Roll) {
    document.getElementById("headtext").textContent = "Player 2 Wins!";
}
else {
    document.getElementById("headtext").textContent = "Its a Tie!";
}

setImage(dice1Image,player1Roll);
setImage(dice2Image,player2Roll);

So what’s the process here. Let’s skip the function at the top for now. Step one is to roll two dice. This is done with Math.random, which selects a random value between 0 and 1. Dice have 6 sides, so we multiply this by 6, to get a number between 0 and 6. But technically it’s a number between 0 and 5.999999~ into infinity, not 6. And Math.floor rounds things down, so it’s actually between 0 and 5. So we need to add one.

let player1Roll = Math.floor(Math.random()*6)+1;
let player2Roll = Math.floor(Math.random()*6)+1;

After this is some selectors to pick the the two dice images off the page, so they can be updated to reflect the rolls later.

let dice1Image = document.querySelector("img.img1");
let dice2Image = document.querySelector("img.img2");

Next we get to use a conditional chain to determine the winner and update the h1 tag on the webpage to display the winner, or if it was a tie. I’m not going to repeat that code here again.

Lastly we make two calls to that function I mentioned before, the one at the top of the script, that updates the image for each dice. The function takes the document selector, and the value of each roll as an input. It runs the roll value (1-6) through a selector and then sets the dice image on the HTML document to the appropriate image.

Could this code all be cleaner, probably, but it gets the job done and is easy to follow. There is probably a simpler way to set the images. The image selector could also just look for 0-5 mapped to 1-6 instead of adding 1. But what if I needed this function to do more based on these rolls. Maybe it becomes part of a larger game and deals damage or something. Personally, this is why I prefer code like this, that isn’t perfectly compact, because it becomes functionally more useful down the road.

Also, the game is available to play at this link.

Code Project: Fresh RSS to WordPress Digest

I actually briefly mentioned this project when I write about moving from TinyTinyRSS to FreshRSS. This has become a bit of an evolving and ongoing project however, so I’ve decided to catalogue it in it’s own page. This little script worked out much better than I expected, and I’ve modified it a bit over time, and have ideas to modify it going forward even more. Starting off, the code can be found here in this Github GIST.

I’ve left a bit of commented out code that i might use later for troubleshooting or adding additional features. The general gist of the code, it pulls the last 24 hours worth of news stories I have favorited from my FreshRSS install, then formats them into a digest format and posts it here, in this blog. They get sorted into their own category, you can find them here.

This is basically a thing I’ve seen others do that I’ve wanted to do for a while. It’s also partially just for my reference more than anything, it’s sort of a log of everything I have found interesting on a particular day more than anything. Others may or may not find it interest, which is why I also filter that category out of the home page feed.

Originally, it was just a list of URLs and titles. I realized that it might be useful to have SOME idea what the link was about before clicking it, so I have been playing with the summary as well. My first attempt was a bit dodgy because it actually posted the entire article as the summary. Currently, it just arbitrarily chops it off at a few hundred characters. I want to improve it even farther at some point by pushing it through some summarizing AI and getting an actual proper summary but I have not gotten there yet.

There re a few other things I want to add but I’m not sure they re easily possible. Firstly, I would love to be able to parse some sort of categories into the digest. So say, all the “Video Game” links are together and Music links are together. FreshRSS has categories but they don’t seem to show up in the feed anywhere.

This would also allow me to split these posts between this blog and my other blog, Lameazoid. I do share interesting video games news from FreshRSS, but I mostly don’t share Toy related articles, because it feels a little TOO FAR out there for what I want to post to this blog. If there were a way to have the categories, I could easily have the script split the feed by categories and post a digest to each blog.

I also wish there was a way to add my own notes and commentary occasionally. I don’t think it showed up in the feed either, but TinyTinyRSS had a notes feature. I am not sure if FreshRSS has that as well. I probably should try to at least suggest these features to the creators on GitHub, or maybe get really adventurous and create my own plug-ins for FreshRSS to accomplish these tasks.