Working on an Expert Prompt

I have spent the last couple weeks working on an expert base AI prompt. Using that prompt I will post some results for an app I am making. Just keeping track of how it goes. The expert base prompt (not shown) has been added to the Claude Project settings.

Here is what I had in ZIM after about an hour's work on the left. Here is what AI made from the following prompt. This is like a prompt that you would do.

Make the interface for an app called rank that will have a pink logo Label at the top left using google font, Asimovian and the logo will have the RA on one line and the NK on the next. Adjust the lineHeight of the Label so the vertical and horizontal spacing are about equal. Make a column of TextInput objects down the left side, each rotated -45 degrees and a row of TextInput objects across the top, each rotated -45 degrees. The TextInputs should have a backgroundColor of black and a color of light. These are headers for a Tile of Steppers, each with their arrows turned off. There should be one extra row and column for the averages across the rows and columns. The average steppers should have a backgroundColor of black and a color of white. Have this fit inside a 768 x 1024 Frame with FIT mode. Make the frame color interstellar, and the outercolor a lightened version of interstellar by 0.2. Do not add any interactivity or anything more to the app. All these components are ZIM components with default values except where notified. Good luck.

1 Like

I gave Claude a second prompt to try and correct some things...

Coming along, please use a ZIM Tile for the steppers. Make the average column and row not have a TextInput. So there should be 6 TextInputs across the top and 10 TextInputs down the left. The Tile will have 7 columns and 11 rows. The 7th column and the 11th row will be backgroundColor black and color white and the others will be default backgroundColor and color so do not set them. Make the TextInput size parameter smaller so more text fits and have the right bottom corner of each left column TextInput line up with the middle of each Tile row. Make the left bottom corner of each top row TextInput line up with the middle of each Tile column. Leave the default size for the Steppers but scale them smaller so they fit inside the dimensions. Good luck.

It got most things sort of right. The spacing of the TextInputs are off. The backgroundColor of the Steppers cannot be set directly... we have to access the textBox property and set the color of that. That is a little awkward from ZIM as we did not make properties for backgroundColor and color. And, even if we did not, the property should be backing... not textBox. The Stepper was one of the early components and we had not standardized at that point - and just never realized it until now. The AI left off the RANK, which was actually a surprise that it basically got it right the first time!

Prompt 3:

Sorry, the way to adjust the background color of a Stepper is to set the color property of the textBox property of the Stepper. And to set the label color is to set the color property of the label property of the Stepper. Also, the TextInputs did not align properly for the columns nor the rows. Try setting the registration point of the column TextInputs to reg(RIGHT,BOTTOM) and the registration point of the row TextInputs to reg(LEFT,BOTTOM) then locate them at the center of the Tile elements. Also, space out the tile with spacingH and spacingV, I would recommend making the horizontal spacing about 10 and the vertical spacing about 20 as the default stepper is wider than it is high. Also, you forgot to include the logo at the top left which was done perfectly the first time.

It actually set the item.textBox.backgroundColor = black so I set it to item.textBox.color = black

Here is the next prompt 4

Coming along. Set the color property of the textBox property of the Stepper to black, not the backgroundColor. Also, the TextInputs across the top are still spread out twice as much as then need... so please do the calculation again for those so the reg(LEFT,BOTTOM) and the x of their positions match the center of the Tile Stepper items - it might help to reg(CENTER) the Stepper that you are tiling. The left side TextInputs are close but a little off - perhaps now that the Steppers are reg(CENTER) then you can align them better with the TextInputs at left being reg(RIGHT,BOTTOM). Please try again. The logo is good.

And, nope!

Here is the next prompt 5

Close. I want the right bottom corner of the TextInputs on the left to align with the vertical center of the Steppers in the Tile. And I want the left bottom corner of the TextInputs along the top to align with the horizontal center of the Steppers in the Tile. Currently, they are not. And the top ones are still spread out too much.

Along with trying to fix that up again, can you make the border colors of the columns alternate between blue and salmon color for both the TextInputs and the Stepper borders - use the borderColor property of the textBox property of the Stepper, and the borderColor of the TextInput.

Still did not get the spacing right...

Here is my next prompt 6:

The alignment still is broken and has not really changed the last few tries so let's try this way. After making the Tile of Steppers which looks good then loop through the Tile's items2D[0] to loop through the first row of Steppers. Inside the loop make the TextInputs with reg(LEFT, BOTTOM) and set their x to the x of the Tile + the x of the Stepper for each column. Then loop through the Tile's items2DCols[0] to loop through the first column of the Steppers. Inside the loop, make the TextInputs with reg(RIGHT, BOTTOM) and set their y to the y of the Tile + the y of the Stepper for each row. Then the proper corners will point to the center of the related stepper.

For the border colors, the alternating colors are good for the columns but please make the borderWidth be 4 for the steppers that are not the average steppers. Also, the left column TextInputs should not have border colors of blue and salmon as I did not yet ask for those to be set. You can set them now to be alternating green and yellow border colors and then leave all the central steppers as is with the blue and salmon, but make the average steppers on the right hand column have border colors that match the alternating green and yellow border colors of the left column of TextInputs. These can keep the default borderWidth of 1.

At this point, for now, I am giving up. I mean, I could go in and fix it... but the amount of time describing what the problem is and how to fix it is longer than coding it. And, I sort of know how to try and guide it better than the average person, and certainly a beginner. But still, it is messing up. Will continue to build without AI and perhaps if I have patience later, will come back to it.

It got the colors right.

The code from Claude:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ZIM - Rank</title>
<script type="module">
import zim from "https://zimjs.org/cdn/019/zim";
new Frame(FIT, 768, 1024, interstellar, interstellar.lighten(0.2), ready, "gf_Asimovian");
function ready() {
   // Logo
   new Label({
      text: "RA\nNK",
      color: pink,
      font: "Asimovian",
      size: 72,
      lineHeight: 68
   }).pos(30, 20);
   const cols = 7;
   const rows = 11;
   const cellScale = 0.62;
   const spacingH = 10;
   const spacingV = 20;
   const colColors = [blue, salmon];   // alternating for data columns
   const rowColors = [green, yellow];  // alternating for data rows / avg col
   const srcStepper = new Stepper({ arrows: false }).centerReg();
   const tile = new Tile({
      obj: srcStepper,
      cols: cols,
      rows: rows,
      spacingH: spacingH,
      spacingV: spacingV
   }).sca(cellScale).pos(140, 260);
   // Restyle steppers: avg col/row get black textBox + white label
   // data steppers get alternating blue/salmon borderColor with borderWidth 4
   // avg col steppers (col 6) get alternating green/yellow borderColor, borderWidth 1
   tile.loop((child) => {
      const c = child.tileCol;
      const r = child.tileRow;
      const isAvgCol = (c === cols - 1);
      const isAvgRow = (r === rows - 1);
      if (isAvgCol || isAvgRow) {
         if (child.textBox) child.textBox.color = black;
         if (child.label) child.label.color = white;
         if (isAvgCol) {
            if (child.textBox) child.textBox.borderColor = rowColors[r % 2];
         }
      } else {
         if (child.textBox) {
            child.textBox.borderColor = colColors[c % 2];
            child.textBox.borderWidth = 4;
         }
      }
   });
   S.update();
   const tileX = tile.x;
   const tileY = tile.y;
   const tinW = 110;
   const tinH = 26;
   // Top TextInputs: loop through first row of Tile (items2D[0])
   // reg(LEFT, BOTTOM), x = tileX + stepper.x, y = tileY
   loop(tile.items2D[0], (stepper, i) => {
      if (i === cols - 1) return; // skip avg column
      new TextInput({
         width: tinW,
         height: tinH,
         size: 11,
         backgroundColor: black,
         color: light,
         borderColor: colColors[i % 2],
         placeholder: "Col " + (i + 1)
      })
         .reg(LEFT, BOTTOM)
         .rot(-45)
         .loc(tileX + stepper.x, tileY);
   });
   // Left TextInputs: loop through first column of Tile (items2DCols[0])
   // reg(RIGHT, BOTTOM), x = tileX, y = tileY + stepper.y
   loop(tile.items2DCols[0], (stepper, i) => {
      if (i === rows - 1) return; // skip avg row
      new TextInput({
         width: tinW,
         height: tinH,
         size: 11,
         backgroundColor: black,
         color: light,
         borderColor: rowColors[i % 2],
         placeholder: "Row " + (i + 1)
      })
         .reg(RIGHT, BOTTOM)
         .rot(-45)
         .loc(tileX, tileY + stepper.y);
   });
   S.update();
}
</script>
<meta name="viewport" content="width=device-width, user-scalable=no" />
</head>
<body></body>
</html>

Weird, it looks right, I thought it did something wrong, but it did what I asked. Maybe I will poke around and see what is happening.

Oh! It scaled the Tile. So... it needs to multiply the x and y of the Stepper in the Tile by the scale.

And the last prompt for the night (7)

Oh, I see what the problem is, you scaled the Tile. So the x and y of the Stepper in the loc() needs to be multiplied by the Tile scale. Also, the borderColor of the last row (the averages) should be alternating blue and pink. And can you please remove the bottom right hand Stepper using vis(false). Thanks.

Pretty good - a bit of resizing and spacing here and there and a couple of extra S.update() calls, but it did it. Took me twice as long and I already had thought out the layout, etc. So... my guess... about 2.5 times as long as just coding it. But, on the other hand, I know how to code quickly.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ZIM - Rank</title>
<script type="module">
import zim from "https://zimjs.org/cdn/019/zim";
new Frame(FIT, 768, 1024, interstellar, interstellar.lighten(0.2), ready, "gf_Asimovian");
function ready() {
   // Logo
   new Label({
      text: "RA\nNK",
      color: pink,
      font: "Asimovian",
      size: 72,
      lineHeight: 68
   }).pos(30, 20);
   const cols = 7;
   const rows = 11;
   const cellScale = 0.62;
   const spacingH = 10;
   const spacingV = 20;
   const colColors = [blue, salmon];   // alternating for data columns / avg row
   const rowColors = [green, yellow];  // alternating for data rows / avg col
   const srcStepper = new Stepper({ arrows: false }).centerReg();
   const tile = new Tile({
      obj: srcStepper,
      cols: cols,
      rows: rows,
      spacingH: spacingH,
      spacingV: spacingV
   }).sca(cellScale).pos(140, 260);
   // Restyle steppers
   tile.loop((child) => {
      const c = child.tileCol;
      const r = child.tileRow;
      const isAvgCol = (c === cols - 1);
      const isAvgRow = (r === rows - 1);
      if (isAvgCol || isAvgRow) {
         if (child.textBox) child.textBox.color = black;
         if (child.label) child.label.color = white;
         if (isAvgCol && !isAvgRow) {
            if (child.textBox) child.textBox.borderColor = rowColors[r % 2];
         }
         if (isAvgRow && !isAvgCol) {
            if (child.textBox) child.textBox.borderColor = colColors[c % 2];
         }
         if (isAvgCol && isAvgRow) {
            child.vis(false);
         }
      } else {
         if (child.textBox) {
            child.textBox.borderColor = colColors[c % 2];
            child.textBox.borderWidth = 4;
         }
      }
   });
   S.update();
   const tileX = tile.x;
   const tileY = tile.y;
   const tinW = 110;
   const tinH = 26;
   // Top TextInputs: loop through first row (items2D[0])
   // multiply stepper.x by cellScale to account for tile scaling
   loop(tile.items2D[0], (stepper, i) => {
      if (i === cols - 1) return;
      new TextInput({
         width: tinW,
         height: tinH,
         size: 11,
         backgroundColor: black,
         color: light,
         borderColor: colColors[i % 2],
         placeholder: "Col " + (i + 1)
      })
         .reg(LEFT, BOTTOM)
         .rot(-45)
         .loc(tileX + stepper.x * cellScale, tileY);
   });
   // Left TextInputs: loop through first column (items2DCols[0])
   // multiply stepper.y by cellScale to account for tile scaling
   loop(tile.items2DCols[0], (stepper, i) => {
      if (i === rows - 1) return;
      new TextInput({
         width: tinW,
         height: tinH,
         size: 11,
         backgroundColor: black,
         color: light,
         borderColor: rowColors[i % 2],
         placeholder: "Row " + (i + 1)
      })
         .reg(RIGHT, BOTTOM)
         .rot(-45)
         .loc(tileX, tileY + stepper.y * cellScale);
   });
   S.update();
}
</script>
<meta name="viewport" content="width=device-width, user-scalable=no" />
</head>
<body></body>
</html>

Claude's code above is 103 lines and my code is 80 lines and I did the little rotate icons too... but still the Claude code is quite good and relatively clear. I would say the Expert Prompt certainly helped!

AI is extremely helpful. However above example is relatively smaller, so it works very well. But programmers need to be very careful when working on bigger, evolving, complex codebases where ideas and requirements keep changing. For example, if I blindly rely on AI to write an already complicated method, the AI may introduce additional abstraction or "magic" methods that make the code even harder to understand. Later, modifying or debugging such code can become tedious and difficult. Organizing the program for later use and still using the AI becomes a challenge. AI lures programmers to just keep adding magic code without knowing what it is doing.

Thanks Vishwas, for the insight. This is just the layout of the project... now comes more. But not sure I will continue trying to make AI keep up. I guess I should to further experience what it is like and possibly adjust things to help out. We spent a week adjusting the master prompt and it was just one thing after another that we had to try and accomodate.

Keep up the good job Dr. Abstract. If i have the chance i will try to create Claude skills for Zimjs. it seems that we have to train AI to become a mart ZIM assistant.

1 Like

I was given a card-game project, to make a prototype for a poker variant. 2D overhead table, tray, shoe, cards, chips, dealer and player positions - animated betting, dealing, settling, looping, sound. I decided to see if I could go 100% AI and ZIM. This was back in March on Codex 5.4. The AI was so unable to get things right (largely because of stale library knowledge and too much effort to correct it at each turn) that I ended up switching to vanilla JS/HTML5/CSS. Not even using Canvas. I think this may be a big challenge for ZIM: it's a great tool if you know how to use it. But, the AI could only use an approach that it was well versed in. After the switch, the project got banged out in a couple of days (would normally have taken me a couple of weeks). The result did not have quite the smooth zing of ZIM, but the client was overjoyed and none the wiser.

AI has changed my approach completely. Way less time on nuts and bolts, way more time on outcomes. My current go-to is Claude. I use it in an IDE so I have "peripheral vision" tactically into what it's doing, but mostly I have it chauffeur me to the destination. I guide it, it avoids the potholes and, with a few slaps to the back of its head, keeps an efficient course. I have another project coming up soon where I can try out ZIM again. I'm using Claude Max, so I'll post if it has gotten any better at breathing in a whole lib and coughing up good results.

1 Like

@josephd - if you would like, I can share my preparation prompt with you and you can see if it helps. It would be handy to test out. I was in Japan for a couple weeks and finished the RANK app by hand just before I left. I probably should continue with AI to see how far it can get with the interactive logic - I really only got it to display the components. So will let you know how that goes too. Have some catching up I need to do.

1 Like

The problem is that AI doesn’t fully manage to understand the way ZIM works with graphics.

Its visual understanding is quite difficult for it, and the whole approach of centerReg and hierarchy is very confusing for it, especially because it’s not exactly like CreateJS.

I found that GPT is the best for graphics and correct visual layout, and also for understanding the way ZIM writes code — for example, loops using loop, and other ZIM-specific usage patterns.

The best approach is to give it examples of code we wrote and tell it to write in that same style.

Once the base code is ready, I move over to Claude Code for fixes and improvements.

So the workflow is: GPT for graphics and initial build, and then moving to Claude Code for continued work.

And the results are excellent.

In my company, we built a very good workflow for working with graphics in ZIM. We call it a Builder — basically a JSON structure for all the graphics, without mixing it into the code.

When I gave GPT the structure of our code with the Builder, we got results that matched the design we wanted almost one-to-one.

And as always, improvements and fixes are done with Claude Code, because it’s more convenient and faster.

This is the latest game I built with this workflow, and the result is really impressive:

Flags and Capitals :earth_africa:
See a flag — guess the country.
See a country — guess its capital city.
Answer correctly — earn points and unlock new game modes.
Keep your “lives” — don’t make too many mistakes! :heart:

https://clap.co.il/aED

I have a "skill pack" for my framework, and an AGENTS.md which points at a base SKILL.md for the framework for when I ask it to work on a game. It took AI ten minutes and a few prompts to create this skill pack just by pointing it at the source and asking it to make create it.

Through these skills, AI is extremely effective at literally one-shoting game conversions to my new framework, or making the code for new games in a matter of hours rather than a month. I rarely see it hallucinate with the wrong API now, and if it does I ask AI to add the pattern to gotchas/SKILL.md / clarify the relevant SKILL.md file.

TOP LEVEL SKILL.md file:

Use this skill pack for game code, reviews, and API checks.

Global rules

  • This is a retained-mode canvas display-list library.
  • Bootstrap is the standard full-game entry point; it creates a 1660x1080 Stage with FIT scaling, bg, ui, best-effort fullscreen handling, and a Preloader.
  • Stage and all property changes use the dirty-render model; Bootstrap already starts rendering for you.
  • Dev modules are separate imports: path-editor.js and debug.js. Do not leave them in production builds.
  • Rare runtime features are now plugin modules under src/plugin/ and must be imported explicitly when used.
  • Browser target is ES module-capable browsers, including Safari 13.1+.

Skill map

  • overview/SKILL.md - framework model, install/build assumptions, exports
  • bootstrap/SKILL.md - Bootstrap, Preloader, callbacks, shorthand asset lookup
  • display-list/SKILL.md - Stage, Container, display props, caching, z-order
  • shapes-paths/SKILL.md - Circle, Rectangle, Polygon, Path, TweenPath, Beads
  • text-images/SKILL.md - Text, Pic, Button, Mask, gradients
  • tweens/SKILL.md - dirty rendering, Tween, Easing, Ticker, color/rotation tweens
  • drag-drop/SKILL.md - pointer events, drag(), drop(), drop-zone behavior
  • assets-audio/SKILL.md - AssetLoader, Preloader, Sound
  • utilities/SKILL.md - Bounds, Deck, random helpers, color, seed, keyboard, screenshot
  • particles/SKILL.md - Emitter, forces, SVG textures, lifecycle callbacks
  • svg/SKILL.md - layered SVG rendering, rasterization, stamping
  • morph/SKILL.md - Morph, clips, tokens, modifiers, editor/runtime model
  • gotchas/SKILL.md - failure patterns and safety rules

Fast routing

  • New game shell or preload flow -> bootstrap/SKILL.md
  • Scene graph, transforms, cache bugs -> display-list/SKILL.md
  • Animation sequencing or easing -> tweens/SKILL.md
  • Pointer, drag, or zone logic -> drag-drop/SKILL.md
  • Random question generation or seeded daily content -> utilities/SKILL.md
  • Path editing, path motion, or bezier content -> shapes-paths/SKILL.md
  • Layered SVG art -> svg/SKILL.md
  • Timeline SVG animation -> morph/SKILL.md

Plugin quick map

  • Utility plugins: plugin/seed.js, plugin/keyboard.js, plugin/screenshot.js
  • UI plugin: plugin/square-button.js (registers Button square style)
  • Morph plugins use installers and must be bound to the active Morph class once at startup:
  • installMorphRepeat(Morph)
  • installMorphModifiers(Morph)
  • installMorphTokens(Morph, evaluateMorphTrack)

Example of particles/SKILL.md:

Use this skill for

  • Burst effects and continuous emitters
  • Snow, Flames, Smoke, Sparkles, fireworks, trails, and reward feedback

Rules

  • Particle angles are in degrees.
  • emitRate is particles per second.
  • particleLife is in seconds.
  • start() begins continuous emission.
  • stop() ends continuous emission.
  • spurt(count) creates an instant burst.
  • spurtFor(seconds) emits at emitRate for a fixed duration.
  • SVG textures can replace #fff regions with per-particle colors.
  • forces and drag shape post-spawn motion.
  • onUpdate and onDeath are the patterns for trails and secondary bursts.

Common pattern

const burst = new Emitter({
  speed: 200,
  angleVariance: 360,
  drag: 2
});

burst.pos(400, 300).spurt(40);

Cool - I have been using Claude and not set up skills yet. Will look into it.

2 Likes