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