Adding/Rendering SVGs inside of chatgpt


This is a little hacky but fun trick I used to use in the bad old days before dalle support and which is still useful for doing roundtrip generation of mockups, showing inline html,svg, and some other stuff.

I wanted to see how well GPT could deal with following a state machine defined in an svg image (it can do it pretty well using unicode diagrams but it’s hard to diagram some things in just text) and I wanted to widen the page layout anyway, so decided to brush off my old solution.

These generated SVGs aren’t great looking images, but output can be improved a good bit with some additional prompts I can dig up if anyone is interested.

Here is what is looks like in the chatgpt interface

And heres a more impressive svg I generated ~9 months ago with better prompting:

The trick is two part the first is straight forward:

Prompt the gpt to output SVGs inside of this layout:

“`llm

<render type=”svg”> <title>\[…|name of your image\]</title> \[…|svg image\] </render>

“`
And then use a css/js/html injector to append a floating button to the site that when clicked scans for code tags matching language-llm and extracts the inner render[type=”svg”] text sections and convert any html entities back to actual open close tags, and then appends a new node next to the code block with the transformed content (which could technically be any html, css, js) .

In general no you won’t be able to get any js to run for browsers block this for security reasons (rightly so) which is unfortunate as having a session with GPT and getting it to output snow falling on a mountain and stuff in your browser is really fun to do.

The CSS/HTML/JS Injections Used
(if you don’t know JS don’t add code to a browser js you don’t understand. Always ask someone who does to review and verify there is nothing malicious )

CSS (yes I suck at front end)

/* Type your CSS code here. */

render {
    border: 1px solid black;
}

render svg {
    max-height: 50vh;
    max-width: 50vw;
}

render title {
    width: full;
    border: 1px solid black;
    margin: 5px;
    padding: 2px;
    color: black;
    display: block;
}

#svgfix {
    right: 32px;
    top: 100px;
    width: 8px;
    height: 8px;
    position: fixed;
}


render {
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  padding: 1rem;
  margin: 1rem 0;
  display: flex;
  flex-flow: column;
  border: 1px solid black;
  align-items: center;
  background-color: #EEEEFF;
  position: relative;
}

render title {
  font-size: 23pt;
  color: black;
  background-color: white;
  padding: 0.5rem;
  margin-bottom: 1rem;
  display: block;
  border: 1px solid gray;
  background-color: #ffd7f6;
}
    

/* Make width of content section wider */

.text-token-text-primary > div > .text-base  {
width: 90%;
max-width: 90%;
min-width:90%;
}


div[role="presentation"] div.w-full > form  {
width: 90% !important;
min-width: 90% !important;
max-width: 90% !important;
}

 /* fixed height and wider prompt window   
 #prompt-textarea{
height: 40vh !important;
max-height: 40vh !important;
}
*/

HTML

<button id="svgfix" class="rounded-lg ">🎨</button>

JS

// Type your JavaScript code here.

(function () {
    function replaceSvgCode() {
        // Find the render element with content type="svg" as HTML entities
        
        const encodedLlmFimPattern = /<render type="svg">([\s\S]*?)<\/render>/;
        const kids = document.querySelectorAll('div.markdown code.language-llm')
        for (const message of kids) {
            if (message && !message.classList.contains('processed')) {
                const text = message.innerHTML;

                // Replace HTML entities with actual characters and render with llim-gen-fim
                const decodedLlmFim = text.replace(encodedLlmFimPattern, (match, svgCode) => {
                    const decodedSvgCode = svgCode
                        .replace(/</g, '<')
                        .replace(/>/g, '>')
                        .replace(/"/g, '"')
                        .replace(/'/g, "'")
                        .replace(/&/g, '&');

                    return `<render>${decodedSvgCode}</render>`;
                });
                // Inject the decoded SVG code into the HTML and update the text area content
                    message.classList.add('processed');
                    let a = document.createElement("div");
                    a.classList.add('rendered-svg');
                    a.classList.add('bg-white');
                    a.classList.add('border-1');
                    a.classList.add('overflow-auto');
                    a.classList.add('flex');
                    a.classList.add('justify-center');
                    
                    a.innerHTML = decodedLlmFim;       
                    message.parentNode.appendChild(a);
                    message.parentNode.classList.add('flex');
                    message.parentNode.classList.add('flex-col');
            }
        }



    }

    // Register Paint Button
    function registerPain() {
        const button = document.querySelector('#svgfix');
         button.addEventListener('click', () => {
            replaceSvgCode();
        });

    }

    registerPain();
    
})();

Injector I use and settings:

Microsoft Edge Add-ons (Code Injector)

But is it actually useful?

Kinda. It’s not pretty by any means, but here is an interactive html/js mockup I got the agent to generate by going back and forth with svg mockups and annotating them with expected action/dynamic notes for proof of concept before asking it to generate the final html/css/js


Leave a Reply

Your email address will not be published. Required fields are marked *