Button guide
A bulk image resizer for your marketing team from a Python script upload a ZIP, pick a size, download the results
You can write a short Python script with Pillow on your own machine and use Button (https://button.bitgate.dev) to turn it into a web form for your marketing team. They upload a ZIP of up to 25 MB, pick a width and format in the browser, and download the resized batch without installing anything. It's $0 on the Free plan for 1 published tool, up to 3 invited colleagues and 100 runs per month.
Write the Pillow script
Each file input on a Button form holds one file, with up to 8 per run, so taking a single ZIP file as input is the simplest way to let your team resize a whole folder at once. Make the script take its inputs as command-line arguments with argparse instead of hard-coded paths or interactive prompts, take the input file path as an argument, and write the resulting ZIP into an output folder that's also passed as an argument.
Here's a complete script. Save it as resize_batch.py and install Pillow in your Python environment (pip install Pillow).
import argparse
import os
import tempfile
import zipfile
from PIL import Image
parser = argparse.ArgumentParser()
parser.add_argument("--input-zip", required=True)
parser.add_argument("--width", type=int, required=True)
parser.add_argument("--format", choices=["webp", "jpeg"], required=True)
parser.add_argument("--quality", type=int, default=85)
parser.add_argument("--output-dir", required=True)
args = parser.parse_args()
out_zip_path = os.path.join(args.output_dir, f"resized_{args.width}w_{args.format}.zip")
with tempfile.TemporaryDirectory() as tmp_in, zipfile.ZipFile(args.input_zip, "r") as zin:
zin.extractall(tmp_in)
with zipfile.ZipFile(out_zip_path, "w", zipfile.ZIP_DEFLATED) as zout:
for root, _, files in os.walk(tmp_in):
for name in files:
if name.startswith("."):
continue
src = os.path.join(root, name)
try:
with Image.open(src) as im:
ratio = args.width / float(im.width)
new_height = int(float(im.height) * ratio)
resized = im.resize((args.width, new_height), Image.Resampling.LANCZOS)
if args.format == "jpeg" and resized.mode in ("RGBA", "P"):
resized = resized.convert("RGB")
base = os.path.splitext(name)[0]
ext = "jpg" if args.format == "jpeg" else "webp"
buf_path = os.path.join(tmp_in, f"{base}.{ext}")
resized.save(buf_path, args.format.upper(), quality=args.quality)
zout.write(buf_path, arcname=f"{base}.{ext}")
os.remove(buf_path)
except Exception as e:
print(f"Skipping {name}: {e}")
print(f"Saved {out_zip_path}")
Notice --quality has a default. You can either expose it as a number field on the form or keep it as a fixed flag in the command you approve (so colleagues only supply the values for the form fields you show them).
Turn it into a web form with Button
Button runs the job on your computer, never in Button's cloud, while Button's servers relay the form values and files between your colleague's browser and your machine. If you already use R, Node, PowerShell, shell scripts or a compiled binary for image processing, those work too.
- Sign in at https://button.bitgate.dev.
- Install the Button runner on your computer (macOS, Linux or Windows) using the one-line command the site shows you - it's a
curlcommand on macOS and Linux, or PowerShell on Windows. - Open the local setup page and pick your
resize_batch.pyscript. - Set up the inputs that map to your script's arguments:
- A file field for the input ZIP (
--input-zip). - A number field for the target width (
--width). - A choice field for the output format (
--format) withwebpandjpegas the options. - Optionally, a number field for
--quality, or leave--quality 85fixed in the command you approve.
- A file field for the input ZIP (
- Approve the command on your computer and publish.
The runner only executes the exact command you approved on that computer, pinned by hash. Every form value is passed to your program as its own argument with no shell, and a value can't start with -, so nobody can sneak extra flags into your script. If you edit the tool later, your colleagues have to confirm the new version before running it.
Once published, Button gives the tool its own page. Your colleagues open the link from their invite email, sign in with Google or a one-time email code, fill in the form, press Run and download the output ZIP. They don't install Python, they don't touch the terminal, and they never pay.
You get a log of every run showing who ran it and when, and you can set someone's access to expire or revoke it whenever you want.
Limits and pricing
Because jobs run on your own machine, your computer has to be on and online when the marketing team uses the tool. The tool page shows them whether your machine is Ready, Busy or Offline, and a computer runs one job at a time. Each workspace pairs with one computer (runner) on every plan, and pairing a new computer replaces the previous one.
| Limit or plan detail | Free ($0) | Pro ($15/month, or $144/year at $12/month) |
|---|---|---|
| Published tools | 1 | 50 |
| Invited people (you don't count) | 3 | 250 |
| Runs per month (includes your own) | 100 | 5,000 |
| File transfer per month | 1 GB | 50 GB |
| Max size per file | 25 MB | 25 MB |
| Max files and total size per run | 8 files, 100 MB | 8 files, 100 MB |
| Max run duration | 30 minutes | 30 minutes |
| Max log size per run | 1 MB | 1 MB |
| Retention on Button servers | Inputs deleted after 24 hours, outputs after 7 days | Inputs deleted after 24 hours, outputs after 7 days |
If you ever move from Pro back to Free, downgrading deletes nothing.
When to use something else
Button isn't always the right fit.
- If your laptop is closed half the day and you don't have a desktop or spare machine you leave on, your team will hit an Offline page whenever you're away.
- If a single ZIP file regularly exceeds 25 MB (or 100 MB across multiple files in a run), you'll hit Button's file size limit.
- If they only resize a couple of images once in a while and don't need specific Pillow logic, a public online image resizer in the browser is faster than setting up a script, provided you're comfortable uploading those images to a third-party site.
- If your company allows installing software on marketing laptops and everyone wants to work offline with huge local folders, a dedicated desktop batch-resizer app on each person's machine is the better choice.
Use Button when you want your team running your exact script and settings from a browser without anyone installing Python or dependencies on their own machines.
FAQ
Do my colleagues need a Python setup or a paid Button account?
No. They open the link from their invite email in a browser, sign in with Google or a one-time email code, and run the form for free.
Where do the images go when someone runs the tool?
Button's servers relay the form values and the ZIP files between your colleague's browser and your computer, where your Python script actually processes them. Inputs are deleted 24 hours after a run ends, and outputs are deleted after 7 days.
Why use a ZIP file instead of uploading images directly?
Button allows up to 8 files per run (up to 25 MB per file and 100 MB total). Putting a folder of images into a single ZIP file lets your team process more than 8 images in one go, as long as the ZIP itself is 25 MB or under.
What happens if two people click Run at the same time?
A computer runs one job at a time. While a batch is running, the tool page shows Busy, and it goes back to Ready as soon as the run finishes.
Can I add other options like a watermark or fixed aspect ratios?
Yes. It's a normal Python script on your computer, so you can write whatever Pillow code you want and add text, number, yes/no, choice or files fields in Button to pass those options into your script as arguments.
Last updated
Put your script behind a form
Button turns a script on your computer into a web form. Colleagues sign in, fill it in, press Run and download the results. Free for 1 tool and 3 people.