Skip to content

Instantly share code, notes, and snippets.

@kafkasl
Last active March 21, 2026 14:51
Show Gist options
  • Select an option

  • Save kafkasl/c13d41406bc9ac73651eee60446b3bcc to your computer and use it in GitHub Desktop.

Select an option

Save kafkasl/c13d41406bc9ac73651eee60446b3bcc to your computer and use it in GitHub Desktop.
Use your phone to take pictures and share them w/ SolveIT directly

πŸ“Έ Snap β€” Phone Camera for Solveit

Instantly take photos from your phone and access them in any dialog.

How it works

  1. A tiny FastHTML server runs on port 6000, serving a camera capture page
  2. Open the page on your phone browser, tap to snap β€” photo auto-uploads to the instance
  3. Call snap() in any dialog to display the latest photo

Setup (one-time)

1. Create the server notebook: /AUTORUN/snap_server

This runs automatically on instance startup. It needs one code cell:

from fasthtml.common import *
from fasthtml.jupyter import JupyUvi
from pathlib import Path
from datetime import datetime
from PIL import Image as PILImage
import io

PHOTO_DIR = Path('/app/data/photos')
PHOTO_DIR.mkdir(exist_ok=True)

app = FastHTML(hdrs=[Meta(name='viewport', content='width=device-width, initial-scale=1')])
rt = app.route

@rt('/cam')
def cam():
    return Title('πŸ“Έ Snap'), Main(
        H2('πŸ“Έ Snap Photo', style='text-align:center'),
        Form(
            Input(type='file', name='photo', accept='image/*', capture='environment',
                  onchange='this.form.submit()', style='display:none', id='fileinput'),
            Label('Tap to Capture', _for='fileinput',
                  style='display:block;text-align:center;padding:40px;font-size:1.5em;'
                        'background:#4CAF50;color:white;border-radius:12px;cursor:pointer;margin:20px'),
            method='post', action='/cam/upload', enctype='multipart/form-data'),
        Div(id='status', style='text-align:center;padding:20px;font-size:1.2em'),
        style='max-width:400px;margin:auto;padding:20px')

@rt('/cam/upload', methods=['POST'])
async def upload(request):
    form = await request.form()
    photo = form['photo']
    ts = datetime.now().strftime('%Y%m%d_%H%M%S')
    dest = PHOTO_DIR / f'{ts}.jpg'
    content = await photo.read()
    img = PILImage.open(io.BytesIO(content))
    img.thumbnail((1920, 1920))
    img.save(dest, 'JPEG', quality=85)
    return Title('πŸ“Έ Snap'), Main(
        H2('βœ… Uploaded!', style='text-align:center;color:green'),
        Img(src=f'/cam/photo/{ts}', style='max-width:100%;border-radius:8px'),
        A('Take Another', href='/cam',
          style='display:block;text-align:center;padding:20px;font-size:1.2em'),
        style='max-width:400px;margin:auto;padding:20px')

@rt('/cam/photo/{name}')
def photo(name: str):
    from starlette.responses import FileResponse
    return FileResponse(PHOTO_DIR / f'{name}.jpg', media_type='image/jpeg')

srv = JupyUvi(app, port=6001)

2. Add a port mapping: In your instance settings, map a subdomain to port 6000.

3. Bookmark on your phone: Open https://your-port-6000-subdomain.solve.it.com/cam and bookmark it.

4. Optional β€” create a CRAFT at /CRAFTs/snap so any dialog can load snap():

First note cell: describe the CRAFT. Then one code cell:

from pathlib import Path
from IPython.display import Image

PHOTO_DIR = Path('/app/data/photos')

def snap(n=0):
    "Show the nth most recent photo (0=latest). Returns IPython Image."
    photos = sorted(PHOTO_DIR.glob('*.jpg'), reverse=True)
    if not photos: print('No photos yet'); return
    return Image(filename=str(photos[n]), width=600)

Usage

From any dialog (after loading the snap CRAFT):

  • snap() β€” latest photo
  • snap(1) β€” previous photo
  • snap(n) β€” nth most recent
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment