The purpose of this post is to explain how you can create charts, images, or any other visual material you need, either in Bot/templates or directly in your app, that displays data in a different way. The idea is to use a combination of ChatGPT and SVG code. I tested also Gemini to create that SVG code, but ChatGPT is doing a better code for this purpose.
#1 - Ask ChatGPT to create chart view with the details you need. You can also take a screenshot and then ask AI to do the same image but using SVG. Not more complicated than that. In this example I asked: “Create a chart view with 12 colored bars. Y-axis is from 0 to 100 and values in x-axis are months starting from January. Add also a trend line with linear regression. Create this chart using SVG.”
#2 - The chart was looking like this:
#3 - The code you can copy from ChatGPT, as it gives the option to copy it, is showing like this:
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="420"
viewBox="0 0 800 400" preserveAspectRatio="xMidYMid meet" role="img"
aria-labelledby="title desc">
<title id="title">Monthly Values (0–100)</title>
<desc id="desc">Bar chart with 12 colored bars (Jan–Dec), Y-axis 0–100, plus a black linear-regression trend line.</desc>
<defs>
<style>
.sys { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial; fill:#111827 }
.muted { fill:#6b7280 }
.axis { stroke:#111827; stroke-width:1.5 }
.grid { stroke:#e5e7eb; stroke-width:1 }
.tick { fill:#374151; font-size:12px; text-anchor:end }
.xlabel { font-size:12px; fill:#374151; text-anchor:middle }
.title { font-size:16px; font-weight:700 }
</style>
</defs>
<!-- Chart title -->
<text x="60" y="14" class="sys title">Monthly Values</text>
<!-- Gridlines -->
<g class="grid">
<line x1="60" y1="340" x2="780" y2="340" />
<line x1="60" y1="276" x2="780" y2="276" />
<line x1="60" y1="212" x2="780" y2="212" />
<line x1="60" y1="148" x2="780" y2="148" />
<line x1="60" y1="84" x2="780" y2="84" />
<line x1="60" y1="20" x2="780" y2="20" />
</g>
<!-- Axes -->
<line x1="60" y1="20" x2="60" y2="340" class="axis"/>
<line x1="60" y1="340" x2="780" y2="340" class="axis"/>
<!-- Y ticks -->
<g class="sys">
<text x="56" y="343" class="tick">0</text>
<text x="56" y="279" class="tick">20</text>
<text x="56" y="215" class="tick">40</text>
<text x="56" y="151" class="tick">60</text>
<text x="56" y="87" class="tick">80</text>
<text x="56" y="23" class="tick">100</text>
</g>
<!-- Y-axis label -->
<text class="sys muted" font-size="12" transform="rotate(-90 20 200)" x="20" y="200"
text-anchor="middle" dominant-baseline="middle">Value (0–100)</text>
<!-- Bars (values Jan..Dec = [65,50,75,40,90,55,70,30,85,60,45,95]) -->
<g>
<rect x="60" y="132" width="49" height="208" fill="#2563eb" rx="4" />
<rect x="121" y="180" width="49" height="160" fill="#10b981" rx="4" />
<rect x="182" y="100" width="49" height="240" fill="#f59e0b" rx="4" />
<rect x="243" y="212" width="49" height="128" fill="#ef4444" rx="4" />
<rect x="304" y="52" width="49" height="288" fill="#8b5cf6" rx="4" />
<rect x="365" y="164" width="49" height="176" fill="#06b6d4" rx="4" />
<rect x="426" y="116" width="49" height="224" fill="#f97316" rx="4" />
<rect x="487" y="244" width="49" height="96" fill="#22c55e" rx="4" />
<rect x="548" y="68" width="49" height="272" fill="#e11d48" rx="4" />
<rect x="609" y="148" width="49" height="192" fill="#0ea5e9" rx="4" />
<rect x="670" y="196" width="49" height="144" fill="#84cc16" rx="4" />
<rect x="731" y="36" width="49" height="304" fill="#a855f7" rx="4" />
</g>
<!-- Linear regression line (computed on the 12 values, mapped to plot coords)
Data regression: y = 0.839160839x + 57.878787879 (x = month index 1..12)
Pixel map: y_px = 340 - 3.2*y
Endpoints (Jan center x=84.5, Dec center x=755.5):
Jan y_px ≈ 152.103
Dec y_px ≈ 122.564
-->
<line x1="84.5" y1="152.103" x2="755.5" y2="122.564" stroke="#000" stroke-width="2" />
<!-- X labels -->
<g class="sys">
<text x="84.5" y="360" class="xlabel">Jan</text>
<text x="145.5" y="360" class="xlabel">Feb</text>
<text x="206.5" y="360" class="xlabel">Mar</text>
<text x="267.5" y="360" class="xlabel">Apr</text>
<text x="328.5" y="360" class="xlabel">May</text>
<text x="389.5" y="360" class="xlabel">Jun</text>
<text x="450.5" y="360" class="xlabel">Jul</text>
<text x="511.5" y="360" class="xlabel">Aug</text>
<text x="572.5" y="360" class="xlabel">Sep</text>
<text x="633.5" y="360" class="xlabel">Oct</text>
<text x="694.5" y="360" class="xlabel">Nov</text>
<text x="755.5" y="360" class="xlabel">Dec</text>
</g>
</svg>
#4 - For showing this as an image with the browser, you need to substitute all # to %23. That you can do with what ever text editor. Another thing you need to add this code into the beginning exactly: data:image/svg+xml;utf8,
#5 - Now you can test this code with a browser if you paste it as an URL. It should show you the same chart view as an image.
#6 - Lets check some lines from this code and what they are:
<!-- Gridlines -->
This is just a section header / comment and it has nothing to do with the image itself.
<line x1="60" y1="340" x2="780" y2="340" />
This is the first line showing Y-value as 340. The origo is in the upper left corner which means this is the lowest horizontal line. The X-axis is specified later but it´s in the same place.
<!-- Bars (values Jan..Dec = [65,50,75,40,90,55,70,30,85,60,45,95]) --> <g> <rect x="60" y="132" width="49" height="208" fill="#2563eb" rx="4" />
This specify the first bar (on the left). It specifies where it starts (from up to down) and the height show how far it goes down. It should match with the x-axis. The “fill” specifies the color and rx=”4” specifies the radius in the corner. It’s a rectangular object.
#7 - Now the question is how the chart can be dynamic. Lets assume you have a table where the month is selected and we need to sum all values from that column for the January. To make it more easier with the SVG, I assume you have the sum calculated in the app and that column name is [Jan].
You can find out from the code, that the 100 value in the chart is 20 as y-coordinates and if the bar and its height needs to be 100, the height value needs to be 320, which means the y-coordinate is then 340 (20+320). If you are not familiar with functions, ask it again from ChatGTP something like: “When the input is 100, the output is 20. When the input is 0, the output is 340. What is the function?” You will get the proper function where the bar needs to start which is: f(x)=−3.2x+340. The function for the height is then f(x)=3.2x
#8 - How to combine all these together?
Add the expression into the code where it’s needed and the chart is then dynamic:
<rect x="60" y="'&(-3.2*[Jan]+340)&'" width="49" height="'&(3.2*[Jan]&'" fill="#2563eb" rx="4" />
#9 - So what is the purpose to use SVG images?
Think about if you need to show a professional chart with your PDF generated by the Bot. How could you do that and remember it needs to be dynamic? It can’t be then just a screenshot.
When you have the expression in a image column, you can use it in the same way than any other photo / image. And because SVG generates a vector image, it’s clear and sharp on your template.
This is another real dynamic SVG code example on a detail view (The column type is Show/Image):
Have fun!


