Sprites

...of the text variety

This site is hosted on fly.io

It's pretty ok.

I have some gripes around the ux for it, but the cli tooling tends to work and the product overall requires less insanity to get simple things up and running.

The people there have somewhat recently launched a project called sprites.dev

It's sold as stateful sandboxes to safely execute code. Code submitted by users or ai or any other type of miscreant that you wouldn't trust to execute code on a machine you paid money for or invested time into setting up.

Behind the scenes it appears to be firecracker vms with a simple http api on top for interacting with the vm's internals.

org-mode source blocks

org-mode allows working with source code inside special tags.

When running in emacs you can create a source block, write some code in it, and then execute it in the document.

The code in the block runs on your machine and any output it generated can be rendered inside the document based you are editing.

If you are familiar with swift playgrounds or jupyter notebooks this won't seem foreign to you.

Since orgmode.dev is a web app and not actually running in emacs this presents a challenge that I hope sprites.dev can neatly solve.

The backend of this site will create a sprite per user that you can use to execute code for any documents you create on the platform.

Examples

Below are some example snippets showing some simple things that can be done.

If you're logged into orgmode.dev, you'll see a Run button that will execute the code in the block

It looks like this:

88d359d9-104c-42d9-a6ad-a99d0cbf6500

If you want to see the source of this document, just add .org onto the end of the url.

Or just click this if you don't wanna do that.

Python

python
print("hello sprites")

hello sprites

Ruby

ruby
print "hello orgmode.dev"

hello orgmode.dev

Bash

bash
ls -lah

total 48K
drwxr-xr-x 1 sprite sprite 4.0K Feb 19 18:12 .
drwxr-xr-x 1 sprite sprite 4.0K Feb 19 18:12 ..
-rw-r--r-- 1 sprite sprite  750 Feb 19 17:57 .bashrc
drwxr-xr-x 3 sprite sprite 4.0K Feb 19 17:57 .claude
drwxr-xr-x 3 sprite sprite 4.0K Feb 19 17:57 .codex
drwxr-xr-x 4 sprite sprite 4.0K Feb 19 17:57 .config
drwxr-xr-x 4 sprite sprite 4.0K Feb 19 17:57 .cursor
drwxr-xr-x 3 sprite sprite 4.0K Feb 19 17:57 .gemini
-rw-r--r-- 1 sprite sprite   89 Feb 19 17:57 .gitconfig
drwxr-xr-x 6 sprite sprite 4.0K Feb 19 18:11 .local
-rw-r--r-- 1 sprite sprite    0 Feb 19 17:58 .sudo_as_admin_successful
-rw-r--r-- 1 sprite sprite  383 Feb 19 17:57 .tcshrc
-rw-r--r-- 1 sprite sprite 1.1K Feb 19 17:57 .zshrc

Beyond "Hello World"

The above are pretty simple examples of what this site can do. To summarize we can:

  • Create source blocks using snippets by typing <s-Tab to create a block

  • Enter some code in whatever language we pick for the block - highlighting gets applied to rendered output

  • We can execute the code in a sprites.dev vm and pipe the result back into the document we're editing.

Using what we already have here are some slightly more impressive demo

Mandlebrot set (Python)

python
# Mandelbrot set in ASCII
for y in range(-15, 15):
  row = ""
  for x in range(-40, 20):
    c = complex(x/15, y/15)
    z = 0
    for i in range(30):
      z = z*z + c
      if abs(z) > 2: break
    row += " .:-=+*#%@"[min(i, 9)]
  print(row)

               ...::::::::::::-----==*@@@----:::............
              ...::::::::::::-----==+#@@*=----:::...........
             ...::::::::::::-----==+%@@@%+==--::::..........
             ..:::::::::::------=++*@@@@@*===--::::.........
             .:::::::::::-----=++**#@@@@@#*++=--::::........
            .:::::::::::----==+@@%@@@@@@@@@#*#+-:::::.......
            .::::::::::---===+*@@@@@@@@@@@@@@@@=-::::.......
            :::::::::--=====++#@@@@@@@@@@@@@@@#=-:::::......
           .:::::::--======++#@@@@@@@@@@@@@@@@*+--::::......
           .:::::--=+@*******%@@@@@@@@@@@@@@@@%#--::::......
           :::----==+@@@@@%#%@@@@@@@@@@@@@@@@@@#=-:::::.....
           :-----===*%@@@@@@@@@@@@@@@@@@@@@@@@@*=-:::::.....
           :----===+*@@@@@@@@@@@@@@@@@@@@@@@@@@+=--::::.....
           ----=++*@%@@@@@@@@@@@@@@@@@@@@@@@@@@+=--::::.....
           ===*++*%@@@@@@@@@@@@@@@@@@@@@@@@@@@*==--::::.....
          @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%*==--:::::....
           ===*++*%@@@@@@@@@@@@@@@@@@@@@@@@@@@*==--::::.....
           ----=++*@%@@@@@@@@@@@@@@@@@@@@@@@@@@+=--::::.....
           :----===+*@@@@@@@@@@@@@@@@@@@@@@@@@@+=--::::.....
           :-----===*%@@@@@@@@@@@@@@@@@@@@@@@@@*=-:::::.....
           :::----==+@@@@@%#%@@@@@@@@@@@@@@@@@@#=-:::::.....
           .:::::--=+@*******%@@@@@@@@@@@@@@@@%#--::::......
           .:::::::--======++#@@@@@@@@@@@@@@@@*+--::::......
            :::::::::--=====++#@@@@@@@@@@@@@@@#=-:::::......
            .::::::::::---===+*@@@@@@@@@@@@@@@@=-::::.......
            .:::::::::::----==+@@%@@@@@@@@@#*#+-:::::.......
             .:::::::::::-----=++**#@@@@@#*++=--::::........
             ..:::::::::::------=++*@@@@@*===--::::.........
             ...::::::::::::-----==+%@@@%+==--::::..........
              ...::::::::::::-----==+#@@*=----:::...........

ASCII Bar Chart (Python)

python
data = {"Python": 85, "Rust": 72, "JS": 68, "Ruby": 45, "Bash": 30}
max_val = max(data.values())
for lang, val in sorted(data.items(), key=lambda x: -x[1]):
  bar = "█" * int(val / max_val * 4)
  print(f"{lang:>8} │{bar} {val}")

  Python │████ 85
    Rust │███ 72
      JS │███ 68
    Ruby │██ 45
    Bash │█ 30

System Introspection (Bash)

bash
echo "=== Sprite Environment ==="
 uname -a
 echo "---"
 cat /etc/os-release 2>/dev/null | head -3
 echo "---"
 echo "CPU: $(nproc) cores"
 echo "Memory: $(free -h 2>/dev/null | awk '/Mem/{print $2}' || echo 'N/A')"
 echo "Disk: $(df -h / | awk 'NR==2{print $4}') free"

=== Sprite Environment ===
Linux org-editor-7aea4c5f-d3cf-4ebe-be5b-0559bedf38c3 6.12.47-fly #1 SMP PREEMPT_DYNAMIC Fri Feb 13 00:38:38 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
---
PRETTY_NAME="Ubuntu 25.04"
NAME="Ubuntu"
VERSION_ID="25.04"
---
CPU: 8 cores
Memory: 15Gi
Disk: 99G free

Better source block parsing

:exports code (should show code only, no results div)

python
print("you should see this code but no results area")                                 

:exports results (should hide code, show only results after Run)

:exports none (should be completely invisible)

:exports both (default, same as no arg)

python
print("code AND results visible")                                                     

code AND results visible

:results output (default — escaped, pre-wrapped)

python
print("<b>this should be escaped</b>")                                                

<b>this should be escaped</b>

:results output html (raw HTML rendering)

python
print("<h3 style='color: #89b4fa;'>Hello from Python!</h3>")
print("<ul><li>Item one</li><li>Item two</li></ul>")                                  

Hello from Python!

  • Item one
  • Item two

:results output html (SVG)

python
print('<svg width="200" height="100" xmlns="http://www.w3.org/2000/svg">')            
print('  <rect width="200" height="100" rx="15" fill="#313244"/>')                    
print('  <text x="100" y="55" text-anchor="middle" fill="#cdd6f4" font-size="16">SVG from Python</text>')                                                                  
print('</svg>')                                                                       

SVG from Python

:results output table (TSV → table rendering)

python
print("Name\tAge\tCity")                                                              
print("Alice\t30\tSeattle")                               
print("Bob\t25\tPortland")                                                            
print("Carol\t35\tDenver")                                                            

NameAgeCity
Alice30Seattle
Bob25Portland
Carol35Denver

Python value (should print 3, no print() needed)

python
1 + 2                                                                                 

3

Python value with statements

python
xs = [1, 2, 3, 4, 5]
sum(x**2 for x in xs)                                                                 

55

JavaScript value

js
[1, 2, 3].map(x => x * 10)                                                            

[
  10,
  20,
  30
]

Ruby value

ruby
(1..5).map { |n| n ** 2 }                                 

1
4
9
16
25

Named data source

python — greeting
print("Hello, World!")                                    

Hello, World!

Block that references the named block

python
print(f"Received: {msg}")                                                             
print(f"Length: {len(msg.strip())}")                                                  

Received: Hello, World!
Length: 13

Chained computation

python — numbers
print("1,2,3,4,5")                                        

1,2,3,4,5
python
nums = [int(x) for x in data.strip().split(",")]
print(f"Sum: {sum(nums)}")                                                            
print(f"Mean: {sum(nums)/len(nums)}")                                                 

Sum: 15
Mean: 3.0

Value + HTML (return an SVG as value)

js
`<svg width="150" height="50" xmlns="http://www.w3.org/2000/svg">                     
  <circle cx="25" cy="25" r="20" fill="#f38ba8"/>                                     
  <circle cx="75" cy="25" r="20" fill="#a6e3a1"/>                                     
  <circle cx="125" cy="25" r="20" fill="#89b4fa"/>                                    
</svg>`                                                                               

Static results with :results html

python
print("<em>rendered as HTML</em>")                                                    

rendered as HTML