Agent GIF - Sharing Intent

I have had some feedback on this project.

I should make it clear on the purpose and content of the GIF Animation.


Purpose: It is for the agent to explain its purpose to the human user in an animation.

Content: The GIF animation can contain anything that explains the process the agent is designed to achieve to the user. There is no set format for this display.


While I prefer to lay out my images in a similar tree format to the data it holds this is not a requirement.


You need to have some vibe coding skill to create AgentGIFs and more to read them but that barrier is fast falling.


Easiest Route:


**Vibe Coder**

Image Capture: Prnt Scrn frames and save to png/gif file
Create GIF: Chat GPT

I wont detail vibe coder extraction… Vibe coders shouldn’t just download and start messing with data chunks random files into their ChatGPT client! Someone might start injecting memories… For you there is an animation :slight_smile: .


**Kiddy Scripter**

(I hope this is an appropriate form, it is simple tested working code, available as is)

Below is maybe the fastest/easiest way I can think of for a programmer to create an Agent GIF (Screen Recording).

Here is o3 VS 2022 code that records screen or takes screenshots to PNG stream in 'frames' directory...
using System;
using System.Diagnostics;
using System.Drawing;             // ← NEW
using System.Drawing.Imaging;     // ← NEW
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using static System.Net.Mime.MediaTypeNames;

namespace ScreenRecorder
{
    class Program
    {
        // Win32 constants
        const int SM_CXSCREEN = 0;
        const int SM_CYSCREEN = 1;
        const int SRCCOPY = 0x00CC0020;
        const int WM_HOTKEY = 0x0312;
        const uint MOD_ALT = 0x0001;
        const uint MOD_CONTROL = 0x0002;
        const int HOTKEY_ID_RECORD = 1;
        const int HOTKEY_ID_EXIT = 2;
        const int HOTKEY_ID_SNAPSHOT = 3;
        const uint PM_REMOVE = 0x0001;
        const int SM_XVIRTUALSCREEN = 76;
        const int SM_YVIRTUALSCREEN = 77;
        const int SM_CXVIRTUALSCREEN = 78;
        const int SM_CYVIRTUALSCREEN = 79;

        // Output folder
        const string OUTPUT_DIR = @"frames";

        // user32.dll imports
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool UnregisterHotKey(IntPtr hWnd, int id);
        [DllImport("user32.dll")]
        static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
        [DllImport("user32.dll", SetLastError = true)]
        static extern int GetSystemMetrics(int nIndex);
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr GetDC(IntPtr hWnd);
        [DllImport("user32.dll", SetLastError = true)]
        static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

        // gdi32.dll imports
        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr CreateCompatibleDC(IntPtr hdc);
        [DllImport("gdi32.dll", SetLastError = true)]
        static extern bool DeleteDC(IntPtr hdc);
        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
        [DllImport("gdi32.dll", SetLastError = true)]
        static extern bool DeleteObject(IntPtr hObject);
        [DllImport("gdi32.dll", SetLastError = true)]
        static extern bool BitBlt(
            IntPtr hdcDest, int xDest, int yDest, int w, int h,
            IntPtr hdcSrc, int xSrc, int ySrc, int rop);
        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr CreateDIBSection(
            IntPtr hdc, [In] ref BITMAPINFO pbmi,
            uint usage, out IntPtr ppvBits,
            IntPtr hSec, uint off);

        // MSG & POINT for PeekMessage
        [StructLayout(LayoutKind.Sequential)]
        struct POINT { public int x, y; }

        [StructLayout(LayoutKind.Sequential)]
        struct MSG
        {
            public IntPtr hwnd;
            public uint message;
            public UIntPtr wParam;
            public IntPtr lParam;
            public uint time;
            public POINT pt;
        }

        // BITMAPINFOHEADER + BITMAPINFO
        [StructLayout(LayoutKind.Sequential)]
        struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public uint biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
            public uint[] bmiColors;
        }

        static void Main()
        {
            Console.WriteLine("Agent GIF Screen Recorder 20 fps → BMP + PNG");
            Console.WriteLine("Global hotkeys: Ctrl+Alt+R start/stop, Ctrl+Alt+S snapshot, Esc exit\n");

            // register hotkeys
            if (!RegisterHotKey(IntPtr.Zero, HOTKEY_ID_RECORD, MOD_CONTROL | MOD_ALT, (uint)ConsoleKey.R))
                throw new Exception("Could not register Ctrl+Alt+R");
            if (!RegisterHotKey(IntPtr.Zero, HOTKEY_ID_SNAPSHOT, MOD_CONTROL | MOD_ALT, (uint)ConsoleKey.S))
                throw new Exception("Could not register Ctrl+Alt+S");
            if (!RegisterHotKey(IntPtr.Zero, HOTKEY_ID_EXIT, 0, (uint)ConsoleKey.Escape))
                throw new Exception("Could not register Esc");

            try
            {
                // primary monitor is always rooted at (0,0)
                int x = 0;
                int y = 0;
                int width = GetSystemMetrics(SM_CXSCREEN);   // width of primary
                int height = GetSystemMetrics(SM_CYSCREEN);   // height of primary

                int bpp = 24;
                int stride = ((width * bpp + 31) / 32) * 4;

                IntPtr hScreenDC = GetDC(IntPtr.Zero);
                IntPtr hMemDC = CreateCompatibleDC(hScreenDC);

                var bi = new BITMAPINFO
                {
                    bmiHeader = new BITMAPINFOHEADER
                    {
                        biSize = (uint)Marshal.SizeOf<BITMAPINFOHEADER>(),
                        biWidth = width,
                        biHeight = -height,   // top-down
                        biPlanes = 1,
                        biBitCount = (ushort)bpp,
                        biCompression = 0,         // BI_RGB
                        biSizeImage = (uint)(stride * height)
                    },
                    bmiColors = new uint[256]
                };

                IntPtr dibBits;
                IntPtr hBmp = CreateDIBSection(hScreenDC, ref bi, 0, out dibBits, IntPtr.Zero, 0);
                if (hBmp == IntPtr.Zero) throw new Exception("Failed to CreateDIBSection");
                IntPtr hOld = SelectObject(hMemDC, hBmp);

                // prepare output folder
                Directory.CreateDirectory(OUTPUT_DIR);

                // main loop
                bool recording = false;
                int frameIndex = 0;
                long frameInterval = 1000 / 20;   // 20 fps
                var sw = new Stopwatch();
                MSG msg;

                while (true)
                {
                    // drain all pending messages
                    while (PeekMessage(out msg, IntPtr.Zero, 0, 0, PM_REMOVE))
                    {
                        if (msg.message == WM_HOTKEY)
                        {
                            int id = (int)msg.wParam;
                            if (id == HOTKEY_ID_EXIT) goto CLEANUP;

                            if (id == HOTKEY_ID_RECORD)
                            {
                                recording = !recording;
                                if (recording)
                                {
                                    frameIndex = 0;
                                    sw.Restart();
                                    Console.WriteLine("→ Recording started");
                                }
                                else
                                {
                                    sw.Stop();
                                    Console.WriteLine("■ Recording stopped");
                                }
                            }
                            else if (id == HOTKEY_ID_SNAPSHOT)
                            {
                                // take one snapshot
                                BitBlt(hMemDC, 0, 0, width, height, hScreenDC, x, y, SRCCOPY);

                                byte[] buffer = new byte[stride * height];
                                Marshal.Copy(dibBits, buffer, 0, buffer.Length);

                                string ts = DateTime.Now.ToString("yyyyMMdd_HHmmssfff");
                                string bmpName = Path.Combine(OUTPUT_DIR, $"snapshot_{ts}.bmp");
                                //string pngName = Path.Combine(OUTPUT_DIR, $"snapshot_{ts}.png");

                                // SaveBmp(bmpName, width, height, stride, buffer);
                                string pngName = Path.Combine(OUTPUT_DIR, $"{frameIndex}.png");
                                SavePng(pngName, hBmp);

                                frameIndex++;

                                Console.WriteLine($"→ Snapshot saved: {pngName}");
                            }
                        }
                    }

                    // capture next frame if it’s time
                    if (recording)
                    {
                        long elapsed = sw.ElapsedMilliseconds;
                        long nextFrame = frameIndex * frameInterval;
                        if (elapsed >= nextFrame)
                        {
                            BitBlt(hMemDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);

                            byte[] buffer = new byte[stride * height];
                            Marshal.Copy(dibBits, buffer, 0, buffer.Length);

                            // BMP (kept for compatibility)
                            // string bmpName = Path.Combine(OUTPUT_DIR, $"frame_{frameIndex:D5}.bmp");
                            // SaveBmp(bmpName, width, height, stride, buffer);

                            // PNG numbered 0.png, 1.png, ...
                            string pngName = Path.Combine(OUTPUT_DIR, $"{frameIndex}.png");
                            SavePng(pngName, hBmp);

                            frameIndex++;
                        }
                        else
                        {
                            Thread.Sleep(1);
                        }
                    }
                    else
                    {
                        Thread.Sleep(50);
                    }
                }

            CLEANUP:
                Console.WriteLine("Exiting…");
                // cleanup GDI
                SelectObject(hMemDC, hOld);
                DeleteObject(hBmp);
                DeleteDC(hMemDC);
                ReleaseDC(IntPtr.Zero, hScreenDC);
            }
            finally
            {
                // always unregister hotkeys
                UnregisterHotKey(IntPtr.Zero, HOTKEY_ID_RECORD);
                UnregisterHotKey(IntPtr.Zero, HOTKEY_ID_SNAPSHOT);
                UnregisterHotKey(IntPtr.Zero, HOTKEY_ID_EXIT);
            }
        }

        static void SaveBmp(string filename, int w, int h, int stride, byte[] pixels)
        {
            using var fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
            using var bw = new BinaryWriter(fs);

            const int fileHeaderSize = 14;
            const int infoHeaderSize = 40;
            int offset = fileHeaderSize + infoHeaderSize;
            int fileSize = offset + pixels.Length;

            // BITMAPFILEHEADER
            bw.Write((ushort)0x4D42);    // 'BM'
            bw.Write(fileSize);
            bw.Write((ushort)0);
            bw.Write((ushort)0);
            bw.Write(offset);

            // BITMAPINFOHEADER
            bw.Write(infoHeaderSize);
            bw.Write(w);
            bw.Write(-h);                // negative = top-down
            bw.Write((ushort)1);
            bw.Write((ushort)24);
            bw.Write(0u);
            bw.Write((uint)pixels.Length);
            bw.Write(0);
            bw.Write(0);
            bw.Write(0u);
            bw.Write(0u);

            // Pixel data
            bw.Write(pixels);
        }

        static void SavePng(string filename, IntPtr hBitmap)
        {
            // System.Drawing.Image.FromHbitmap copies the pixels,
            // so the original HBITMAP remains valid.
            using Bitmap bmp = System.Drawing.Image.FromHbitmap(hBitmap, IntPtr.Zero);
            bmp.Save(filename, ImageFormat.Png);
        }
    }
}

If you use ChatGPT then you can write a script in your language of choice to turn that .png stream into an animated GIF… You will loose color depth in this process so the PNG stream can be useful and lossless.

And that’s your process:

Screen → GIF → +Agent JSON → Post → (optional) sandbox & inspect JSON

→ Record Screen
→ Convert To GIF
→ Add Agent GIF Application Extension JSON AI Agent
→ Post GIF Online To Forums/Email etc

If you can sandbox Agent GIF then extracting the JSON payload is trivial.


**Professional**

Well you guys should be setting an example…

Piping your process to GIF is not rocket science!

I am building my AI code to be transparent. It is not a perfect method but there are not many methods with this global reach with almost zero infrastructure!


A cat and a robot are playing cat's cradle together with a red string. (Captioned by AI)

Panes can contain code in any language ie Convo… They are language agnostic.

The image is completely blank with a white background. (Captioned by AI)

Convo Code
Section: HandleFormSubmission
    > define Model = "o3";

    // When the form is submitted with a code payload…
    @condition = (FormID == "xPhorm" && Code)
        // If the user asked for the full report…
        @condition = Report
            > assistant: "Explain the quality of this code" with input Code using Model;
            > assistant: "Show me a process tree of this code" with input Code using Model;
            > assistant: "Review the following PHP code and suggest the best next addition:\n
" + Code + "
" using Model;
            > assistant: "Review the following PHP code, find the biggest bug, and explain how to fix it:\n
" + Code + "
" using Model;
            > assistant: "Review the following PHP code, find the biggest security issue, and summarize it:\n
" + Code + "
" using Model;
            > assistant: "Return the full code with all of the above issues addressed:\n
\n" + Code + "\n
\n\n// Include prior findings\n
Addition\n" + lastReply(3) + "\n
\n
Bugs\n" + lastReply(4) + "\n
\n
Security\n" + lastReply(5) + "\n
" using Model;
            > assistant: "Render report panes with titles [\"Code Quality\",\"Code Process\",\"Addition\",\"Bugs\",\"Security\",\"Result\"] and contents of the last six replies, defaulting to \"Code Process\"";

        // If the user asked a custom request instead…
        @condition = Request
            > assistant: Request with input Code using Model;
            > assistant: "HTML-escape and wrap in <pre>:" + lastReply();

        // Always return the assembled browser panes
        > assistant: "Return HTML: <a href=\"/\">Home</a> + BrowserPaneArrays('Developer', panes, true, false, '100%', defaultPane)";


Section: RenderForm
    // Build up the POST form for xPhorm
    @condition = (FormID == "xPhorm")
        > assistant: "Form fields = [\n  {type:'textarea', label:'Request', name:'Request', rows:10, cols:280, value:Request},\n  {type:'textarea', label:'Code',    name:'Code',    rows:50, cols:280, value:Code},\n  {type:'checkbox', label:'Report', name:'Report', checked:true},\n  {type:'submit',   value:'Analyse Code'}\n]";
        > assistant: "Return HTML: <a href=\"/\">Home</a> + HTMLForm(formDef)";

    // If not yet submitted, show the same form by default
    @else
        > assistant: "Render the same form as above";
        > assistant: "Return HTML: <a href=\"/\">Home</a> + HTMLForm(formDef)";

In plain terms, the snippet in the GIF creates a tiny web-tool that lets you paste PHP code and get an instant AI-driven review.

  1. RenderForm section (the “front page”)
  • Builds an HTML form called xPhorm with four controls:
    • Request (free-text prompt)
    • Code (the PHP you want analysed)
    • Report (checkbox, ticked by default)
    • Submit button “Analyse Code”
  • If you open the page without submitting anything, the same form is shown.
  1. HandleFormSubmission section (what happens after you press Analyse Code)
  • Sets Model = "o3" (the AI model to use).
  • Checks that the form is xPhorm and that some Code was supplied.
  • Two possible paths:
    • Report = checked (the normal case)
      It fires six back-to-back AI prompts:
      1. “Explain the quality of this code.”
      2. “Show me a process tree of this code.”
      3. “Suggest the best next addition.”
      4. “Find the biggest bug and show how to fix it.”
      5. “Find the biggest security issue and summarise it.”
      6. “Return the full code with all of the above issues addressed,” including earlier answers.
        Then it bundles the six answers into six browser panes titled Code Quality, Code Process, Addition, Bugs, Security, Result, defaulting the view to Code Process.
    • Report = unchecked (custom request)
      Whatever you typed in Request is sent as-is to the AI along with the code.
      The reply is HTML-escaped and wrapped in a <pre> block.
  • Finally, regardless of path, it returns the assembled panes plus a “Home” link.

So, in one sentence: the script displays a form, feeds your PHP into the AI for an automated multi-pane review when you tick “Report,” or runs a custom prompt when you don’t, and then shows the results in a mini browser-style interface.

Why do this?

Well, maybe it’s good practice to start somewhere. I guess most people know what a GIF is.

Maybe think of it as a training exercise?


I guess it’s kinda like the evals framework that @_j asked me to design…