{"slug": "have-your-agent-record-video-demos-of-its-work-with-shot-scraper-video", "title": "Have your agent record video demos of its work with shot-scraper video", "summary": "Simon Willison released shot-scraper 1.10 with a new `video` command that records Playwright-driven video demos of web applications from a storyboard YAML file, enabling coding agents to produce visual proof of their work. The tool was demonstrated by creating a video of a new Datasette feature for bulk-inserting rows and creating tables from pasted CSV data.", "body_md": "[shot-scraper video](https://shot-scraper.datasette.io/en/stable/video.html) is a new command introduced in today's [shot-scraper 1.10](https://github.com/simonw/shot-scraper/releases/tag/1.10) release which accepts a `storyboard.yml`\n\nfile defining a routine to run against a web application and uses Playwright to record a video of that routine. I've written before about the importance of [having coding agents produce demos](https://simonwillison.net/2026/Feb/10/showboat-and-rodney/#proving-code-actually-works) of their work; this is my latest attempt at enabling them to do that.\n\nHere's an example video created using `shot-scraper video`\n\n, exercising a [still in development](https://github.com/simonw/datasette/pull/2813) feature adding the ability to create new tables in Datasette from pasted CSV, TSV or JSON data:\n\nThat video was created [by running this command](https://simonwillison.net/atom/everything/):\n\n```\nshot-scraper video datasette-bulk-insert-storyboard.yml \\\n  --auth datasette-demo-auth.json --mp4\n```\n\n(That `--auth`\n\nJSON file [contains a cookie](https://gist.github.com/simonw/287b26aff53fcb72942b19f5b69d7e5c), as [described here](https://shot-scraper.datasette.io/en/stable/authentication.html) in the documentation.)\n\nHere's the `datasette-bulk-insert-storyboard.yml`\n\nfile:\n\n```\noutput: /tmp/datasette-bulk-insert-demo.webm\nserver:\n  - uv\n  - --directory\n  - /Users/simon/Dropbox/dev/datasette\n  - run\n  - datasette\n  - -p\n  - 6419\n  - --root\n  - --secret\n  - \"1\"\n  - /tmp/demo.db\nurl: http://127.0.0.1:6419/demo/tasks\nviewport:\n  width: 1280\n  height: 720\ncursor: true\nwait_for: 'button[data-table-action=\"insert-row\"]'\njavascript: |\n  (() => {\n    let clipboardText = \"\";\n    Object.defineProperty(navigator, \"clipboard\", {\n      configurable: true,\n      get: () => ({\n        writeText: async (text) => {\n          clipboardText = String(text);\n        },\n        readText: async () => clipboardText,\n      }),\n    });\n  })();\nscenes:\n  - name: Bulk insert existing table rows\n    do:\n      - pause: 0.8\n      - click: 'button[data-table-action=\"insert-row\"]'\n      - wait_for: \"#row-edit-dialog[open]\"\n      - pause: 0.5\n      - click: \".row-edit-bulk-insert\"\n      - wait_for: \".row-edit-bulk-textarea\"\n      - pause: 0.5\n      - click: \".row-edit-copy-template\"\n      - wait_for: \"text=Copied\"\n      - pause: 0.8\n      - fill:\n          into: \".row-edit-bulk-textarea\"\n          text: |\n            title,owner,status,priority,notes\n            Prepare release video,Ana,doing,1,Recorded with shot-scraper\n            Check pasted CSV import,Ben,review,3,Previewed before inserting\n            Share the branch demo,Chen,queued,2,Bulk insert creates three rows\n      - pause: 0.8\n      - click: \".row-edit-save\"\n      - wait_for: \"text=Previewing 3 rows.\"\n      - pause: 1.2\n      - click: \".row-edit-save\"\n      - wait_for: \"text=3 rows inserted.\"\n      - pause: 1.0\n      - click: \".row-edit-cancel\"\n      - wait_for: \"text=Prepare release video\"\n      - pause: 1.0\n  - name: Create a table from pasted CSV\n    open: http://127.0.0.1:6419/demo\n    wait_for: 'details.actions-menu-links summary'\n    do:\n      - pause: 0.8\n      - click: 'details.actions-menu-links summary'\n      - click: 'button[data-database-action=\"create-table\"]'\n      - wait_for: \"#table-create-dialog[open]\"\n      - pause: 0.5\n      - fill:\n          into: \".table-create-table-name\"\n          text: \"launch_metrics\"\n      - click: \".table-create-from-data\"\n      - wait_for: \".table-create-data-textarea\"\n      - pause: 0.5\n      - fill:\n          into: \".table-create-data-textarea\"\n          text: |\n            metric_id,name,score,recorded_on\n            m001,Activation rate,87.5,2026-06-29\n            m002,Retention check,72.25,2026-06-30\n            m003,CSV import health,95,2026-07-01\n      - pause: 0.8\n      - click: \".table-create-save\"\n      - wait_for: \"text=Previewing 3 rows.\"\n      - pause: 1.2\n      - click: \".table-create-save\"\n      - wait_for_url: \"**/demo/launch_metrics\"\n      - wait_for: \"text=Activation rate\"\n      - pause: 1.2\n```\n\nThe [video command documentation](https://shot-scraper.datasette.io/en/stable/video.html) includes simpler examples, but for the purpose of this post I thought I'd go with something more comprehensive.\n\nThat demo YAML storyboard was constructed entirely by GPT-5.5 xhigh running in Codex Desktop, using the following prompt run inside my `~/dev/datasette`\n\ncheckout of [this branch](https://github.com/simonw/datasette/commits/b759ea548606bc9bf9a4bf0e33e2d57ead7e0ab8/):\n\n`Review the changes on this branch.`\n\n`cd to ~/dev/shot-scraper and run the command \"uv run shot-scraper video --help\"`\n\n`Now use that new video command to record a video demo of the new features from this branch, including running a \"uv run datasette -p 6419 --root --secret 1 /tmp/demo.db\" development server so you can record the video against a demo DB that you first create.`\n\nNow that I've released the feature the prompt could say \"`run uvx shot-scraper video --help`\n\n\" instead and it should achieve the same result.\n\nI really like this pattern where the `--help`\n\noutput for a command provides enough detail that a coding agent can use it - it works kind of like bundling a `SKILL.md`\n\nfile directly inside the tool. I used the same pattern for [showboat and rodney](https://simonwillison.net/2026/Feb/10/showboat-and-rodney/).\n\n`shot-scraper video`\n\nstarted as an experimental prototype. `shot-scraper`\n\nis built on top of [Playwright](https://playwright.dev/), and the key feature it needed was for Playwright to be able to record video of browser sessions with enough control to create the desired demo.\n\nI first tried this a few years ago and found that the Playwright-produced videos included additional chrome that was useful for debugging a test failure but unwanted for a product demo.\n\nThey fixed that a while ago, but there were still some minor blockers. In particular I was getting [a few white frames at the start of the videos](https://github.com/simonw/shot-scraper/pull/194/changes/c2f3b3a52ba84f2adcf3ad6da4d39c2570328584#issuecomment-4724459369), since the recording mechanism kicked in before the first URL was loaded by the browser.\n\nPlaywright 1.59 added a new [screencast mechanism](https://playwright.dev/python/docs/api/class-screencast) providing much more finely grained control over video recording. This was very nearly what I needed, but the resulting videos were fixed at 800px wide.\n\nI found a [landed PR fixing that](https://github.com/microsoft/playwright/pull/41183) but it wasn't yet in a release. Then yesterday they shipped it in [playwright-python 1.61.0](https://github.com/microsoft/playwright-python/releases/tag/v1.61.0) and I was finally unblocked to finish implementing the feature!\n\nThe code itself was all written by GPT-5.5 xhigh in Codex Desktop. I had it write the documentation as well which gave me a very useful frame for reviewing the design - much of the iteration on the feature came from reviewing that documentation, spotting things that were redundant, inconsistent or confusing, and requesting (or dictating) a better design.\n\nThe YAML format itself was mostly defined by the coding agent. I had it [use Pydantic](https://github.com/simonw/shot-scraper/blob/1.10/shot_scraper/video.py#L24) to both define and validate the format, partly to make the design easier to review.\n\nThis is a great example of the kind of feature that I almost certainly wouldn't have taken on without coding agent support. I filed the [original issue](https://github.com/simonw/shot-scraper/issues/142) in February 2024, and had difficulty finding the necessary time to solve this in amongst all of my other projects.\n\nTags: [projects](https://simonwillison.net/tags/projects), [python](https://simonwillison.net/tags/python), [yaml](https://simonwillison.net/tags/yaml), [ai](https://simonwillison.net/tags/ai), [datasette](https://simonwillison.net/tags/datasette), [playwright](https://simonwillison.net/tags/playwright), [shot-scraper](https://simonwillison.net/tags/shot-scraper), [generative-ai](https://simonwillison.net/tags/generative-ai), [llms](https://simonwillison.net/tags/llms), [pydantic](https://simonwillison.net/tags/pydantic), [coding-agents](https://simonwillison.net/tags/coding-agents), [agentic-engineering](https://simonwillison.net/tags/agentic-engineering)", "url": "https://wpnews.pro/news/have-your-agent-record-video-demos-of-its-work-with-shot-scraper-video", "canonical_source": "https://simonwillison.net/2026/Jun/30/shot-scraper-video/#atom-everything", "published_at": "2026-06-30 16:54:26+00:00", "updated_at": "2026-06-30 17:19:37.572732+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["Simon Willison", "shot-scraper", "Playwright", "Datasette"], "alternates": {"html": "https://wpnews.pro/news/have-your-agent-record-video-demos-of-its-work-with-shot-scraper-video", "markdown": "https://wpnews.pro/news/have-your-agent-record-video-demos-of-its-work-with-shot-scraper-video.md", "text": "https://wpnews.pro/news/have-your-agent-record-video-demos-of-its-work-with-shot-scraper-video.txt", "jsonld": "https://wpnews.pro/news/have-your-agent-record-video-demos-of-its-work-with-shot-scraper-video.jsonld"}}