OS Apps: SVGIO, 3D Prints and WikiGo

Published: Jun 12, 2025 by Isaac Johnson

I saw a link recently to svg2solid which can take multi-colour SVGs and turn them into multi-leveled STL files. This enticed me to want to see how hard it would be to have the AIs create SVG files for me. If I could create an interesting scaled vector graphic (SVG), perhaps it would be fun to take that through conversion to STL then on to my Prusa Mini+ to print in PLA. Besides using a variety of AI tools, I’ll also show how to do this with Gimp and Inkscape which are Open-Source art tools.

The other app I wanted to checkout is Wiki Go which is a very light and fast Go-based containerized wiki. We’ll set it up in both Docker and Kubernetes and explore some of it’s features. We’ll also show how to pair that with MermaidJS for diagrams.

SVG IO

The first site we’ll try is svg.io

/content/images/2025/06/svgio-01.png

I asked for a large crown containing three small princess crowns. Not sure that worked too well

/content/images/2025/06/svgio-02.png

How about a simple word?

/content/images/2025/06/svgio-03.png

Nicht zer gut…

/content/images/2025/06/svgio-04.png

Uploading a photo seems to be a premium feature (starts at $9/mo)

/content/images/2025/06/svgio-05.png

I asked for “a small fishing boat” and got a damn yacht

/content/images/2025/06/svgio-06.png

Since my ask for a side view of a small 14-foot fishing boat rendered another 20 foot trawler, we’ll just go with it as I hit my limit

/content/images/2025/06/svgio-07.png

I downloaded the svg file and then went to svg2solid

/content/images/2025/06/svgio-08.png

I tried a few of my renders, and at first i thought they were skewed

/content/images/2025/06/svgio-09.png

But then I just realized they were in a 3d perspective

/content/images/2025/06/svgio-10.png

Just as it stands I could bring them into a slicer and print if I wanted

/content/images/2025/06/svgio-11.png

One issue I noticed with the SVG2STL tool is it gets a bit odd when you have LOTS of colours

/content/images/2025/06/svgio-12.png

I had hoped to try Neosvg

But it doesn’t even offer a trial let alone a sample render

/content/images/2025/06/svgio-13.png

Gemini and PNG to SVG

Let’s try a simple prompt asking for a 3 colour vector art of a small fishing boat..

I was actually amazed it kicked back actual SVG (I did not even ask for that)

/content/images/2025/06/svgio-14.png

I pasted that into a file

builder@DESKTOP-QADGF36:~$ vi /mnt/c/Users/isaac/Downloads/gemini-boat.svg
builder@DESKTOP-QADGF36:~$ cat /mnt/c/Users/isaac/Downloads/gemini-boat.svg
<svg viewBox="0 0 200 150" xmlns="http://www.w3.org/2000/svg" font-family="Inter, sans-serif">
  <!-- Define Colors -->
  <defs>
    <color id="hullColor">#2c3e50</color> <!-- Dark Blue/Gray -->
    <color id="cabinColor">#3498db</color> <!-- Medium Blue -->
    <color id="accentColor">#e67e22</color> <!-- Orange -->
  </defs>

  <!-- Water (optional, can be background of where it's placed) -->
  <rect width="200" height="150" fill="#ecf0f1" />
  <path d="M 0 110 Q 50 100, 100 110 T 200 110 L 200 150 L 0 150 Z" fill="rgba(52, 152, 219, 0.5)" />
  <path d="M 0 115 Q 50 105, 100 115 T 200 115 L 200 150 L 0 150 Z" fill="rgba(41, 128, 185, 0.6)" />


  <!-- Boat Hull -->
  <path d="M 20 120 Q 30 100, 70 100 L 150 100 Q 180 100, 190 120 L 170 135 L 40 135 Z" fill="url(#hullColor)" stroke="#1c2833" stroke-width="1.5"/>

  <!-- Cabin -->
  <path d="M 70 100 L 70 70 Q 75 65, 80 65 L 120 65 Q 125 65, 130 70 L 130 100 Z" fill="url(#cabinColor)" stroke="#2980b9" stroke-width="1.5"/>

  <!-- Cabin Roof -->
  <rect x="65" y="60" width="70" height="10" rx="2" ry="2" fill="url(#cabinColor)" stroke="#2980b9" stroke-width="1.5"/>

  <!-- Window -->
  <rect x="85" y="75" width="30" height="15" rx="2" ry="2" fill="#ffffff" stroke="#2980b9" stroke-width="1"/>
  <line x1="100" y1="75" x2="100" y2="90" stroke="#2980b9" stroke-width="1"/>
  <line x1="85" y1="82.5" x2="115" y2="82.5" stroke="#2980b9" stroke-width="1"/>


  <!-- Mast -->
  <rect x="55" y="50" width="5" height="50" fill="url(#accentColor)" stroke="#d35400" stroke-width="1"/>

  <!-- Small flag/detail on mast -->
  <polygon points="60,50 75,55 60,60" fill="url(#accentColor)" stroke="#d35400" stroke-width="1"/>

  <!-- Fishing Rod Holder (simple representation) -->
  <rect x="155" y="90" width="5" height="15" rx="1" ry="1" fill="url(#cabinColor)" stroke="#2980b9" stroke-width="1"/>
  <rect x="165" y="90" width="5" height="15" rx="1" ry="1" fill="url(#cabinColor)" stroke="#2980b9" stroke-width="1"/>

  <!-- Boat Name (example - very small) -->
  <text x="90" y="130" font-size="6" fill="#ffffff" text-anchor="middle">WaveRider</text>

  <!-- Deck Line -->
  <line x1="25" y1="120.5" x2="185" y2="120.5" stroke="rgba(0,0,0,0.2)" stroke-width="1"/>

</svg>

I brought that into svg2solid and while not the prettiest boat, at least it fits the brief

/content/images/2025/06/svgio-15.png

Let’s try words now

/content/images/2025/06/svgio-16.png

It didn’t really work. For one, I had to escape the & and then the colours just show as white (both in a browser and via the svg2stl)

/content/images/2025/06/svgio-17.png

Maybe I can nudge it away from text

/content/images/2025/06/svgio-18.png

Much closer

/content/images/2025/06/svgio-19.png

I’ll be honest - after repairing the edges, I kind of like it - it’s funky; broken, but in a fun way

/content/images/2025/06/svgio-20.png

I then added a box and a filimament swap spot

/content/images/2025/06/svgio-21.png

and we can see how it turned out:

/content/images/2025/06/svgio-26.jpg

I printed with a mutli-colour PLA then the top layer is some left-over clear PLA (which can be sanded to clear if I take the time, otherwise it’s just translucent).

I also tried feeding images to Convertio

/content/images/2025/06/svgio-22.png

Which can essentially pair with svg2stl as a black and white art maker

/content/images/2025/06/svgio-23.png

/content/images/2025/06/svgio-24.png

Gimp

Let’s make a quick multi-colour image I’ll use for a Father’s day emblem for my Father-in-law’s fishing box

/content/images/2025/06/svgio-27.png

After exporting to a JPG, I’ll use convertio.co to convert the JPG to an SVG

/content/images/2025/06/svgio-28.png

That actually failed - just made a black oval. However, jpg2svg.com worked great

/content/images/2025/06/svgio-29.png

I’ll now feed that into the svg2solid site

Because I’m going for 3 levels, I set all the variations of red to 20 so we can just see 0, 10, 20 and 30 for heights

/content/images/2025/06/svgio-30.png

However, this created separate STLs for each layer which would be a pain to merge and level as separate entities

/content/images/2025/06/svgio-31.png

imagetostel.com got close, but inverted - handy if i was pouring wax or soap.

/content/images/2025/06/svgio-32.png

However, I can invert output on import

/content/images/2025/06/svgio-33.png

That looks better

/content/images/2025/06/svgio-34.png

I can split to remove the bottom then set level colour changes to print a multicolour emblem

/content/images/2025/06/svgio-35.png

Inkscape

If you use a tool like Illustrator or the Open-Source equivelant, Inkscape, you’ll want to make sure to convert your Text elements to paths or they’ll be ignored on conversion

/content/images/2025/06/svgio-36.png

Interestingly, when imported to Prusa, it was backwards so I had to rotate the text 180 degrees

Because the bottom layer was just a cylinder, I found I could get a smoother result by just ditching the one from the web and adding a shape as a part

/content/images/2025/06/svgio-40.png

/content/images/2025/06/svgio-39.png

Which we can now see printed and affixed to the tackle box:

/content/images/2025/06/20250608_093836.jpg

Wiki Go

A while back I came across this Marius Hosting article on Wiki Go which bills itself as “a modern, feature-rich, databaseless flat-file wiki platform built with Go”.

Let’s start with just a quick Docker invokation

docker run -d \
  --name wiki-go \
  -p 8085:8080 \
  -v "$(pwd)/data:/wiki/data" \
  leomoonstudios/wiki-go

Which fires right up

builder@DESKTOP-QADGF36:~/Workspaces$ mkdir wikiGo
builder@DESKTOP-QADGF36:~/Workspaces$ cd wikiGo/
builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ mkdir data
builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ docker run -d \
/data:/wiki/data>   --name wiki-go \
dios/wiki-go>   -p 8085:8080 \
>   -v "$(pwd)/data:/wiki/data" \
>   leomoonstudios/wiki-go
Unable to find image 'leomoonstudios/wiki-go:latest' locally
latest: Pulling from leomoonstudios/wiki-go
f18232174bc9: Already exists
da5d4b509c5f: Pull complete
2491a069e5f6: Pull complete
9dccb6d0a3e1: Pull complete
662f704ed0b4: Pull complete
274e9733b623: Pull complete
Digest: sha256:793b90094277ce0a6b25283da0f2bc1156a73612790d4ee56449dc38502fb3cb
Status: Downloaded newer image for leomoonstudios/wiki-go:latest
ac4fb803c4df3df722bb0ccba1e5a1034c7039fa3f373a73ab20cfd5895bb6f7

The website loaded very fast

/content/images/2025/06/wikigo-01.png

We can login with the default admin/admin

/content/images/2025/06/wikigo-02.png

and as recommended, immediately go to the Wiki Settings and Users to change our password

/content/images/2025/06/wikigo-03.png

Be aware that the password is stored as a hashed value in the data dir:

builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ cat data/config.yaml | tail -n3 && echo
    - username: admin
      password: $2a$10$lp1Rnno8.dfpG3IW0c2kbuow.FERDi.pUJ.gqfWE3RS3pt5sCD9K.
      role: admin

One of the nice features of Wiki-Go is that we can easily import a zip of markdown using “Import” if we are moving our Wiki from another source (e.g. Azure DevOps Wiki).

/content/images/2025/06/wikigo-04.png

Such as my local mapping of systems

/content/images/2025/06/wikigo-05.png

As kubernetes

We can turn that docker invokation into a simple Kubernetes YAML manifest:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wiki-go-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wiki-go
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wiki-go
  template:
    metadata:
      labels:
        app: wiki-go
    spec:
      containers:
        - name: wiki-go
          image: leomoonstudios/wiki-go:latest
          ports:
            - containerPort: 8080
          volumeMounts:
            - name: data
              mountPath: /wiki/data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: wiki-go-data

---
apiVersion: v1
kind: Service
metadata:
  name: wiki-go
spec:
  type: ClusterIP
  ports:
    - port: 8085
      targetPort: 8080
      protocol: TCP
  selector:
    app: wiki-go

Then apply it

builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ kubectl create ns wikigo
namespace/wikigo created
builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ kubectl apply -f k8s.yaml -n wikigo
persistentvolumeclaim/wiki-go-data created
deployment.apps/wiki-go created
service/wiki-go created

I’ll port-forward to the service and use a different port just to ensure I’m not hitting my local docker version

$ kubectl port-forward svc/wiki-go -n wikigo 8086:8085
Forwarding from 127.0.0.1:8086 -> 8080
Forwarding from [::1]:8086 -> 8080

and it works like a charm

/content/images/2025/06/wikigo-06.png

To expose it externally, let’s create an A record in Azure DNS

$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 75.73.224.240 -n wikigo
{
  "ARecords": [
    {
      "ipv4Address": "75.73.224.240"
    }
  ],
  "TTL": 3600,
  "etag": "8ba366a2-8abb-47bf-9f62-5310ef57139f",
  "fqdn": "wikigo.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/wikigo",
  "name": "wikigo",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "trafficManagementProfile": {},
  "type": "Microsoft.Network/dnszones/A"
}

Next I can add an ingress

builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ cat ./ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: wiki-go
  name: wikigoingress
spec:
  rules:
  - host: wikigo.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: wiki-go
            port:
              number: 8085
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - wikigo.tpk.pw
    secretName: wikigo-tls
builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ kubectl apply -f ingress.yaml -n wikigo
ingress.networking.k8s.io/wikigoingress created

When I see the cert is satisfied

builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ kubectl get cert -n wikigo
NAME         READY   SECRET       AGE
wikigo-tls   False   wikigo-tls   19s
builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ kubectl get cert -n wikigo
NAME         READY   SECRET       AGE
wikigo-tls   False   wikigo-tls   50s
builder@DESKTOP-QADGF36:~/Workspaces/wikiGo$ kubectl get cert -n wikigo
NAME         READY   SECRET       AGE
wikigo-tls   True    wikigo-tls   83s

I can then go to https://wikigo.tpk.pw to login and change the admin password

/content/images/2025/06/wikigo-07.png

One thing I can do in settings is disable the public Wiki

/content/images/2025/06/wikigo-08.png

Once I save, I can see my unauthenticated browser session can no longer view the Wiki

/content/images/2025/06/wikigo-09.png

Diagrams

My first real test of any wiki offering is MermaidJS - will it be able to render diagrams?

Indeed, using the keyword mermaid (not mermaidjs)

/content/images/2025/06/wikigo-11.png

It did just fine

/content/images/2025/06/wikigo-10.png

This makes it really easy to whip up diagrams for our Wiki site

I just yesterday had a great conversation with a former colleague about Diagrams and AI.

One of my “secret weapons” in making things is to ask the AIs for graphs to illustrated points.

For instance, say I wanted to build a diagram of a well architected GCP environment that has data ingestion using PubSub and a backend of GKE and Cloud SQL (PostgreSQL).

Let’s be a bit pushy. Here is the kind of thing I would build out on a day to day:

Create a diagram of a well architected GCP environment that has data ingestion using PubSub and a backend of GKE and CloudSQL (PostgreSQL). We well use CloudDNS with cert-manager in GKE for SSL and HTTPS load-balancers for traffic into GKE. We will have three workloads in GKE - a service for reading from PubSub and processing into CloudSQL, a Reporter that will have an external web site and a Monitor service that detects issues and sends alerts. I also want to include NewRelic as an external APM that uses OpenTelemetry Metrics, Traces and Logs from those services for viewing and alerting through PagerDuty.

Gemini 2.5 Pro came back with Markdown:

/content/images/2025/06/wikigo-12.png

graph TD
    subgraph "External Users"
        direction LR
        User[<fa:fa-user> User]
    end

    subgraph "External Monitoring & Alerting"
        direction LR
        NewRelic[<img src="https://newrelic.com/assets/newrelic/brand/logo-newrelic-02ab29b835948f219159515910f1b241.svg" width="100">]
        PagerDuty[<img src="https://www.pagerduty.com/wp-content/uploads/2021/04/pagerduty-logo-rebrand-2021-stacked-rgb-red.png" width="100">]
    end

    subgraph "Google Cloud Platform (GCP)"
        direction LR
        subgraph "Data Ingestion"
            PubSub[<fa:fa-rss> Cloud Pub/Sub]
        end

        subgraph "GKE Cluster"
            direction TB
            GKE[<fa:fa-cubes> Google Kubernetes Engine]
            subgraph "GKE Workloads"
                direction LR
                PubSubReader["<fa:fa-cogs> Pub/Sub Reader Service"]
                Reporter["<fa:fa-desktop> Reporter Web Service"]
                Monitor["<fa:fa-tachometer-alt> Monitor Service"]
            end

            subgraph "Ingress & SSL"
              direction LR
              Ingress[<fa:fa-network-wired> GKE Ingress]
              CertManager["<fa:fa-id-card> cert-manager"]
            end

            GKE -- Manages --> PubSubReader
            GKE -- Manages --> Reporter
            GKE -- Manages --> Monitor

            Ingress -- Routes Traffic To --> Reporter
            CertManager -- Manages SSL for --> Ingress
        end

        subgraph "Data Storage"
            CloudSQL[<fa:fa-database> Cloud SQL PostgreSQL]
        end

        subgraph "Networking & DNS"
            CloudDNS[<fa:fa-globe> Cloud DNS]
            LoadBalancer[<fa:fa-server> HTTPS Load Balancer]
        end

        subgraph "Observability"
          direction LR
          OpenTelemetry[<fa:fa-chart-line> OpenTelemetry Collector]
        end
    end

    User -- HTTPS --> LoadBalancer
    CloudDNS -- Resolves Domain --> LoadBalancer
    LoadBalancer -- Forwards Traffic --> Ingress

    PubSub -- Pushes Messages --> PubSubReader

    PubSubReader -- Writes Data --> CloudSQL
    Reporter -- Reads Data --> CloudSQL
    Monitor -- Reads Data --> CloudSQL

    PubSubReader -- Sends Metrics, Traces, Logs --> OpenTelemetry
    Reporter -- Sends Metrics, Traces, Logs --> OpenTelemetry
    Monitor -- Sends Metrics, Traces, Logs --> OpenTelemetry

    OpenTelemetry -- Exports Data --> NewRelic
    NewRelic -- Sends Alerts --> PagerDuty
    Monitor -- Sends Alerts --> PagerDuty

    style GKE fill:#4285F4,stroke:#fff,stroke-width:2px,color:#fff
    style PubSub fill:#F4B400,stroke:#fff,stroke-width:2px,color:#fff
    style CloudSQL fill:#DB4437,stroke:#fff,stroke-width:2px,color:#fff
    style CloudDNS fill:#0F9D58,stroke:#fff,stroke-width:2px,color:#fff
    style LoadBalancer fill:#0F9D58,stroke:#fff,stroke-width:2px,color:#fff
    style Ingress fill:#4285F4,stroke:#fff,stroke-width:2px,color:#fff
    style CertManager fill:#4285F4,stroke:#fff,stroke-width:2px,color:#fff
    style OpenTelemetry fill:#000000,stroke:#fff,stroke-width:2px,color:#fff

(note: i did fix a typo in line 44)

/content/images/2025/06/wikigo-13.png

While that worked in Mermaid Live editor, I got an error in wikigo

/content/images/2025/06/wikigo-14.png

I thought I might solve it with a followup prompt

/content/images/2025/06/wikigo-15.png

That said, we can always download the SVG or PNG from the Mermaid playground

/content/images/2025/06/wikigo-16.png

Which would do the job

/content/images/2025/06/wikigo-17.png

Summary

Today we started by exploring 3D Printing SVG art. I tried svg.io which offers to use AI to make SVGs and it did fair. But then I moved on to using Gemini Pro to create my SVGs. In both cases, I would feed the SVGs into svg2solid to create the STL files I would send to my slicer. I also played with using Gimp to create JPGs and PNGs for which I used Convertio and jpg2svg.com to convert the JPG. I also tried another SVG to STL converter, imagetostel.com which worked when the first failed. Lastly, I showed how to make my own clean text with Inkscape, an open-source vector art creator (akin to Illustrator) and turn that into a real print (e.g. “OpaBox”).

In the last half of the article, we looked at Wiki Go and set it up in docker and k8s. I showed how to make it not public and integrate MermaidJS diagrams.

I think the big takeaway for me, surprisingly, was how easy Inkscape is to use. I will be going back to that often.

opensource 3dprint svg stl wikigo gimp inkscape containers kubernetes

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes