---
name: demo-day-traction-sheet
description: "Turn a public demo day recording into a traction sheet with Vivu: each startup's slide figures read from frames, the funding line the founder said, and timestamped links. Use before a partner meeting."
---Demo day traction sheet from pitch recordings
This skill takes the link to a demo day recording that an accelerator or incubator has published and returns a screening sheet for the partner meeting: one row per startup with the traction figures shown on its slides (revenue, growth, customers, users), the time and a saved frame they were read from, what the founder said out loud about funding, copied from the captions, the funding text on the slide next to it, a link to the organizer's recording at that second, and an empty "meet" column for the partners. Claude reads the company list from the organizer's chapters, downloads the pitch section, cuts one file per company, prices the job against the user's Vivu plan, uploads the files to a private Vivu project, runs two precise searches, checks every window against the frames or the captions, fills the gaps with a cheap local contact sheet, and writes the sheet.
The value is in what only the recording has. Most early stage companies do not publish their deck, the event page lists names and one line pitches, and the captions only hold what the founder said. Traction figures often sit on a slide for a few seconds and are never read aloud, and the amount a company is raising is sometimes on the slide but not in the speech. The hard part is that a pitch is full of big numbers that are not traction (market size, pricing, product stats) and full of "traction" slides that show only logos. So every window Vivu returns stays a candidate until a full frame shows figures on a traction slide, and the figures in the sheet come from that frame, never from Vivu's reason text.
When to use
Use when someone asks "pull the traction numbers from last week's demo day", "which companies at this demo day showed revenue, and how much are they raising?", "make me a screening sheet from the accelerator's demo day video", or "go through the batch recording and list each startup's metrics for Monday's partner meeting".
Not for these:
- One company's pitch, or one question about one video ("what did the third startup say about churn?"): search Vivu directly and look at the frame.
- Scoring, ranking or recommending companies, or any investment advice. The sheet places the figures side by side and the partners decide.
- Following one company across quarters. The earnings-commentary-drift-memo skill (https://vivu.ai/skills/earnings-commentary-drift-memo) is built for that.
- Recordings that were not published by the organizer, or that the organizer's terms do not let you analyze.
- A deck the company already sent you as a PDF: read the PDF.
Working principles
- Report measured numbers, not estimates. When a number is an estimate, say so.
- Nothing is verified until it has been checked against the source. A traction window is a candidate until a full frame shows figures on a traction slide; the figures in the sheet are copied from that frame. A funding window is a candidate until the captions show the founder's sentence; the quote is copied from the captions. Vivu's reason text paraphrases and can describe figures that are not on screen, so it never fills a cell.
- Stop and tell the user when a required capability or tool is missing. Do not guess around it.
- Ask the user before anything that is expensive to redo (the indexed minutes, before upload) and before anything that acts on their behalf (posting the sheet or the note anywhere). The skill writes local files only until the user approves a destination.
- A company with no row of figures is reported as "no figures found in the recording", which is a finding about the pitch, not about the company. Never fill a gap from the company's website or from memory.
What you need before starting
Check each item at the start of the run and tell the user plainly what is missing before doing anything else.
| Requirement | Why | How to check |
|---|---|---|
| Vivu connector with write access | create a private project, open its upload page, search | vivu_get_account shows can_create_projects: true (tool names may carry a server prefix). A write call failing with "has not granted vivu.write" means the user reconnects Vivu and allows write access |
| A shell on a machine with a residential IP, with yt-dlp and node | YouTube blocks downloads from cloud and datacenter IPs with "Sign in to confirm you're not a bot"; current yt-dlp needs a JavaScript runtime | yt-dlp --version, node --version, then the chapter command in Step 2 |
| ffmpeg and ffprobe on the same machine | cut one file per company, measure lengths, extract frames and contact sheets | ffmpeg -version, ffprobe -version |
| Captions for the recording (the platform's English captions, or a local speech to text tool such as whisper.cpp) | funding quotes are copied from the captions, never from the reason text | the download in Step 2 writes a .vtt file; open it |
| An upload path | move the files into Vivu | vivu_open_upload_page plus the user's own browser, or a browser tool that can attach local files |
| The demo day link, and the company list (chapters, the organizer's page, or the batch announcement) | one file per company is what lets each result map to a company | the user pastes the link; Step 2 reads the chapters |
This skill needs Claude Code on the user's computer (the terminal or the Code tab of Claude Desktop), because it downloads with yt-dlp from a residential IP, keeps the files locally and runs ffmpeg. Claude on the web and cloud sessions cannot do that. Nothing recurs, so no scheduler is involved; run it once per demo day. The only connector it needs is Vivu; posting the result anywhere uses whichever connector the user names, after approval.
Inputs to collect
Ask for anything missing, most important first.
- DEMO_URL: the organizer's recording. Required.
- The company list with start and end times. Default: the video's chapters. When there are no chapters, the organizer's agenda, or the "Introducing COMPANY" title cards found on a 4 second contact sheet (Step 8 has the command).
- Which companies to include. Default: all of them.
- Where the sheet goes. Default: a local CSV only; anything else (a shared drive, a Notion page, a Slack channel) waits for approval in Step 9.
- WORK_NAME: a short name for the working folder and the Vivu project. Default: the event name and year, for example ndrc-2021.
Files and state
Keep everything in one working folder:
demoday-WORK_NAME/
config.json DEMO_URL, VIDEO_ID, Vivu project id, query wording, destination
companies.csv nn, company, start_s, end_s, file, duration_s, video_id
source/ the downloaded recording and its .vtt captions, plus archive.txt
segments/ one file per company, named NN_COMPANY.mp4
results/ one JSON per search, without the result page link
frames/ full frames and contact sheets the figures were read from
candidates.csv every window Vivu returned, with the verdict and the reason
traction_sheet.csv one row per company
partner_note.md the short note for the partner meeting
state.json steps done, files uploaded, searches run with job ids, windows checked
Run every command from the folder that contains demoday-WORK_NAME/. A rerun reads state.json first: a file marked uploaded is not uploaded again, a search with a saved result is not run again, and a window with a verdict in candidates.csv is not checked again.
Step 1: Check the Vivu connector and the setup
Goal: confirm every requirement before spending anything.
- Call vivu_get_account. If the tool does not exist, tell the user to add the Vivu connector in Claude (
https://mcp.vivu.ai/mcp) and stop. If the account does not showcan_create_projects: true, or a later write call fails with "has not granted vivu.write", ask the user to reconnect Vivu and allow write access, then stop until they have. - Run
yt-dlp --version,node --version,ffmpeg -versionandffprobe -version.
Done when vivu_get_account shows can_create_projects: true and all four commands print a version.
Step 2: Get the recording and the company list
Goal: the pitch section on disk with captions, and one start and end time per company.
- Confirm the Compliance items with the user before downloading.
- Read the chapters, which on organizer recordings usually name each company and its start:
yt-dlp --js-runtimes node --skip-download --print "%(chapters)s" "DEMO_URL"
- Download the recording at 720p with English captions. 720p keeps slide figures readable at a fraction of the 1080p size. VIDEO_ID is the 11 character ID in DEMO_URL; the match filter refuses anything longer than four hours, which is usually a wrong link:
yt-dlp --js-runtimes node --restrict-filenames \
-f "bv*[height<=720][ext=mp4]+ba[ext=m4a]/b[height<=720]" --merge-output-format mp4 \
--write-subs --write-auto-subs --sub-langs en --sub-format vtt \
--match-filter "duration<=14400" \
--download-archive demoday-WORK_NAME/source/archive.txt \
-P "home:demoday-WORK_NAME/source" -o "%(id)s.%(ext)s" --retries 5 \
"https://www.youtube.com/watch?v=VIDEO_ID"
Download the whole file and cut locally. yt-dlp's --download-sections option streams the section through ffmpeg in real time, which was much slower than the full download in our test run. The youtube-competitor-watch skill (https://vivu.ai/skills/youtube-competitor-watch) covers channel verification and download troubleshooting in more depth. 4. Write companies.csv: nn (01, 02, ...), company, start_s and end_s from the chapters. Leave out the host's opening and closing remarks; they are not pitches and they cost index minutes.
Done when the recording and its .vtt file are in source/ and companies.csv has one row per company with a start and an end.
Step 3: Cut one file per company and price the job
Goal: a file for each company, with its length and price approved before upload.
- Cut each company with a re-encode, so the cut points are exact and the file's time plus start_s is the time in the organizer's recording. COMPANY is a short lowercase name with no spaces, so Vivu keeps the file name as it is:
ffmpeg -v error -ss START_S -to END_S -i demoday-WORK_NAME/source/VIDEO_ID.mp4 -c:v libx264 -preset veryfast -crf 23 -c:a aac demoday-WORK_NAME/segments/NN_COMPANY.mp4
- Measure each file and write duration_s:
ffprobe -v error -show_entries format=duration -of csv=p=0 demoday-WORK_NAME/segments/NN_COMPANY.mp4
- Call vivu_get_usage and show one table:
| This demo day | Plan allowance | Remaining this month | |
|---|---|---|---|
| Index minutes | sum of duration_s / 60 | from vivu_get_usage | from vivu_get_usage |
| Search credits | two precise searches, 10 credits, plus 5 for each rewording (estimate) | from vivu_get_usage | from vivu_get_usage |
Plan facts: Free is $0 a month with 20 index minutes a month and 50 search credits a month; Premium is $30 a month with 180 index minutes and 500 search credits. A precise search uses 5 credits in total. A demo day of eleven pitches is about 40 index minutes (estimate), so Free covers about five pitches a month and Premium about four such demo days (estimate).
- If the job does not fit, offer levers in this order: index only the companies the partners care about (their sector or stage), split the batch across two months, and only then move to a larger plan. Never drop a company without naming it, and never trim or recompress a pitch to save minutes.
In our test run the eleven files measured 39.83 minutes. The account used in our test run returns no allowance figures, so the plan table and the user's approval were not exercised in our test run.
Done when every company has a file in segments/ with a duration_s, and the user has approved the minutes and credits.
Step 4: Upload and index
Goal: every company file indexed in a private Vivu project.
- Call vivu_list_projects and reuse a project named "Demo day WORK_NAME" if one exists. Otherwise call vivu_create_project with that name and visibility "private". The default visibility is organization, which shows the project to everyone in the user's Vivu organization, and a fund's screening work usually should not be.
- Call vivu_open_upload_page with the project ID immediately before uploading. The link expires in 180 seconds and works once, so never post or store it. Give it to the user to open in their own browser and select every file in segments/, or open it in a browser tool that can attach local files. Claude in Chrome accepts at most 10 MB per upload call; larger files go through the user's own browser or the Vivu web app. Never split or recompress a file to fit.
- Poll vivu_list_videos until every file shows ready. Vivu replaces spaces and punctuation in file names with underscores, which is why the files are named NN_COMPANY.mp4; match each listed name to companies.csv and record the video_id. When a name does not match, match by length (duration_ms against duration_s).
In our test run all eleven files were ready about 11 minutes after the upload page was opened, and every listed name equaled the local name. The files went through the upload page with an automated browser, so opening the link in the user's own browser was not exercised in our test run.
Done when every row of companies.csv has a video_id and every file shows ready.
Step 5: Search
Goal: saved candidate windows for traction slides and for funding statements.
Two precise searches cover the whole demo day, because every company is in the same project and each file is one company. fast is not used: it returns whole files with an empty reason, and the file list is already known. The chain: the traction search finds where figures are on screen, the frame check in Step 6 reads them, the funding search finds where the founder talks about money (the flip from what is shown to what is said), and the captions in Step 7 give the exact words to set beside the slide.
| Field | Query | Mode | maximum_results |
|---|---|---|---|
| traction_slide | a pitch slide on screen with the startup's own traction written as figures: a revenue amount, a growth percentage, or a count of customers, users, students or sign-ups; a slide that shows only customer or partner logos without figures does not count, and neither does a market size, pricing, funding or team slide | precise | 20 |
| spoken_funding | the founder says out loud how the company is funded: money already raised, or a funding round they are raising or opening now | precise | 20 |
maximum_results 20 suits a demo day of up to about fifteen companies, since each shows one or two traction slides and says one funding sentence; it is also the recall ceiling, so raise it to 40 for a larger batch.
Run vivu_search_videos (project_id, query, mode "precise", maximum_results) for each. It returns a job ID; call vivu_get_search_results until complete is true. Each status call can wait up to 45 seconds, so a pending search is not a stalled one. Save each result to results/FIELD.json without the result page link, and show the result page link only in the live reply: it expires after four hours, so it never goes into the sheet, the note or candidates.csv.
In our test run the traction wording above came after 1 rewording on the same corpus. The first wording returned two slides about traction that showed only logos; naming slides that show only logos as not counting removed both, but the final wording also stopped returning one company the first wording had found (Step 8 recovers such misses).
Done when both result files are saved and their job ids are in state.json.
Step 6: Read the traction figures from full frames
Goal: each traction window labeled kept or rejected, with the figures copied from a full frame.
- Make a contact sheet across the window, one frame every half second. A window often holds two or three slides, and the traction slide may be on screen for only part of it:
ffmpeg -v error -y -ss START -to END -i demoday-WORK_NAME/segments/NN_COMPANY.mp4 -vf "fps=2,scale=320:-1,tile=8x7" -frames:v 1 demoday-WORK_NAME/frames/NN_traction_sheet.png
START and END are the window in seconds (start_ms and end_ms divided by 1000). Tile k is at START plus k/2 seconds, counting from 0 at the top left; a window longer than 28 seconds needs a second sheet from START plus 28. 2. Pick the tile where the traction slide is fully shown and extract it as a full frame:
ffmpeg -v error -y -ss SECONDS -i demoday-WORK_NAME/segments/NN_COMPANY.mp4 -frames:v 1 -q:v 3 demoday-WORK_NAME/frames/NN_traction.png
- Label the window. Kept: a slide about the company's own traction with figures (revenue, growth rate, customers, users, sign-ups, pipeline). Rejected: a slide titled traction that shows only logos, a market size slide, a pricing slide, a product stat ("10x faster"), or a funding slide. Partner reach ("our partner serves N districts") is not the company's own traction; keep it out of traction_figures.
- Copy the figures exactly as written on the frame, units included. Small text in a chart that is not legible at 720p is marked UNCERTAIN; never complete it from the reason text.
- Write every window into candidates.csv with its verdict and a one line reason, and count rejected windows as false positives.
Worked example from our test run
The test corpus was a public demo day recording an accelerator published on its own channel: eleven pre-recorded pitches, slide beside the founder, cut into 11 videos (39.83 minutes). Before searching we went through every pitch on a contact sheet and through the captions, and wrote down which companies show a traction slide with figures (6 of the 11) and which do not (5, most of them with a traction slide of logos only).
The first traction wording returned 6 windows: 4 real and 2 false, with 2 companies missed. Both false windows were traction slides with logos only. After 1 rewording on the same corpus, the final wording returned 4 windows: 4 real, 0 false, and 2 missed. The median window was 21.75 seconds wide. Market size, pricing and product stat slides, which carry the biggest numbers in a pitch, were never returned. In one kept window the slide at the start was the market size slide and the slide at the end was a partner's reach, with the traction slide in the middle; the half second contact sheet is what made that visible. In all true windows the figures in the reason matched the frame, but on other footage the reason has described prices and scores that were not on screen, so the frame stays the only source.
Done when every traction window has a verdict in candidates.csv and each kept window has a frame and its figures.
Step 7: Check the funding sentences in the captions
Goal: each funding window labeled kept or rejected, with the founder's words copied from the captions.
- Convert the window to the organizer's time: segment seconds plus the company's start_s. In a .vtt file the times are on the line above the text, so
grep -n "^00:MM:" demoday-WORK_NAME/source/VIDEO_ID.en.vttfinds the line numbers for minute MM; read from the window's start to its end. - Kept: the founder says how much has been raised, or that a round is open, planned or being raised. Rejected: the host talking about money, or funding text on the slide that nobody says.
- Copy the sentence into spoken_funding_quote. Auto captions mishear amounts and currencies; when the slide shows an amount, read it from a full frame (Step 6 command) and put it in slide_funding_text, so the sheet shows both. Mark a quote NOT VERIFIED when there are no captions.
In our test run the funding search returned 10 windows: 10 real, 0 false, and 1 missed of the 11 funding statements. The median window was 12.25 seconds wide. The host's remarks about money in the closing segment were not returned. The amounts founders said aloud were all money already raised; one founder's slide showed the amount of the new round and the founder never said it, and the captions turned one currency into a different word.
Done when every funding window has a verdict in candidates.csv and each kept window has a quote.
Step 8: Fill the gaps
Goal: every company checked, including the ones the searches did not return.
Recall is the weak side of the traction search, so check each company that has no kept traction window with a contact sheet of its whole pitch, one tile every 4 seconds. This is local and costs no credits:
ffmpeg -v error -y -i demoday-WORK_NAME/segments/NN_COMPANY.mp4 -vf "fps=1/4,scale=320:-1,tile=6x10" -frames:v 1 demoday-WORK_NAME/frames/NN_overview.png
Tile k is at 4 times k seconds. One sheet covers 240 seconds. For a file longer than that, make a second sheet starting at 240 seconds with the same command plus -ss 240 before -i, saved as NN_overview_2.png, where tile k is at 240 plus 4 times k seconds. Find the traction slide on the sheets, extract a full frame at that time with the Step 6 command, and read the figures. Write "no figures found in the recording" only after every sheet for the file has been checked. Mark these rows traction_source "contact sheet" so the partners know the search did not find them. For a company with no kept funding window, grep the captions in its time range for funding words:
grep -n -i -E "rais|round|invest|funding|seed" demoday-WORK_NAME/source/VIDEO_ID.en.vtt
In our test run the contact sheets found the traction slides of the 2 companies the final wording missed and confirmed that the other five pitches show no figures; the caption grep found the funding sentence the search missed.
Done when every company in companies.csv has either figures with a frame or "no figures found in the recording", and either a funding quote or "not said".
Step 9: Write the sheet and the partner note
Goal: a sheet and a short note the partners can read, approved before it goes anywhere.
- Show the user one sample row and the field mapping, and wait for a yes before writing the rest:
company,source_file,slide_time,traction_figures,traction_source,frame,spoken_funding_quote,spoken_time,slide_funding_text,transcript_checked,video_link,meet
COMPANY,07_company.mp4,02:32,"35,000 students; signed PARTNER; distributors selling in US, Latam, APAC",search,frames/07_traction.png,"we're opening a new round and are looking for investors",03:46,"We're opening a new round",yes,https://www.youtube.com/watch?v=VIDEO_ID&t=1866,
| Field | Source | If unavailable |
|---|---|---|
| company | companies.csv (the organizer's chapter) | none |
| source_file | segments/ | none |
| slide_time, spoken_time | the kept frame or caption line, as MM:SS in the company's file | blank |
| traction_figures | copied from the full frame | "no figures found in the recording"; UNCERTAIN for illegible text |
| traction_source | search, or contact sheet (Step 8) | none |
| frame | path of the frame the figures were read from | blank |
| spoken_funding_quote | copied from the captions | "not said"; NOT VERIFIED without captions |
| slide_funding_text | read from a full frame of the funding slide | blank |
| transcript_checked | yes when the quote was read in the captions | no |
| video_link | DEMO_URL with &t= start_s plus the slide time in seconds | the time only |
| meet | left empty for the partners | stays empty |
- Write traction_sheet.csv and partner_note.md. The note lists the companies with figures (one line each, figures as written), the companies without figures, and every place where the slide and the speech disagree. It contains no ranking, score or recommendation.
- Tell the user the counts: companies with figures, companies without, windows rejected, and rows found only by the Step 8 contact sheets.
- Before putting the sheet or the note anywhere outside the working folder (a shared drive, a Notion page, a Slack channel), name the destination, show the full rendered note, and wait for explicit approval. Email goes to drafts unless the user asks to send.
In our test run the sheet has eleven rows. The user's approval of the sample row and posting the note were not exercised in our test run, and nothing was sent.
Done when traction_sheet.csv has one row per company in companies.csv, partner_note.md exists, and the user has approved the sample row and any destination.
Compliance
- Use only recordings the organizer published, and check the platform's terms (YouTube's Terms of Service restrict downloading) and the organizer's terms. Keep the files for internal screening; do not redistribute the video, clips or frames. Confirm this with the user before Step 2.
- The founders chose to pitch on a public recording, but the sheet is internal. Name companies, not people, and do not identify anyone by face or voice.
- The sheet places figures and quotes side by side. It does not score companies or give investment advice, and the note says so.
- The videos stay in the user's Vivu project until the user deletes them. Create the project as private. Delete a project or video only when the user asks, and confirm first.
- Posting the sheet or the note acts as the user; Step 9 asks first.
- Youth entrepreneurship events where minors pitch are out of scope unless the organizer has guardian consent for this use; otherwise do not run the skill on them.
Known failure modes
| Symptom | Cause | Fix |
|---|---|---|
| (observed) a "slide titled Traction with pilot and LOI logos and no figures" is returned as traction | the slide is about traction, and the first wording did not exclude slides that show only logos | the final wording excludes them; Step 6 rejects any left |
| (observed) a kept window starts on a market size slide and ends on a partner's reach slide | the window spans several slides around the traction slide | pick the tile from the half second contact sheet; copy figures only from the traction slide |
| (observed) a company found by an earlier wording is missing from the final results | tightening the wording traded one company for another; recall is the weak side | run the Step 8 contact sheet for every company without a kept window |
| (observed) a funding sentence is not returned: "talking to investors who want to partner with us" | the founder avoids the words raise and round | grep the captions for funding words (Step 8) |
| (observed) the captions write the currency as another word: "year round led by top vcs" | auto captions mishear amounts and units | read the amount from the slide frame; keep both in the sheet |
| (observed) the amount of the round is on the slide but never said: "kicking off our seed round fundraising today" | the founder leaves the number to the slide | fill slide_funding_text from a frame and point out the gap in the note |
| the reason text gives figures that are not on screen | the reason paraphrases and has invented prices and scores on other footage | copy figures only from a full frame |
| an empty result for a company that showed traction | search recall is limited by wording and by maximum_results | the Step 8 contact sheet; an empty result does not prove the pitch had no figures |
| "Sign in to confirm you're not a bot" | download attempted from a cloud IP | run Step 2 on the user's own machine |
| upload page asks to sign in or shows an error | the one time upload link expires after 180 seconds | request a new link right before opening it |
| a file is rejected by the browser upload tool | Claude in Chrome accepts at most 10 MB per upload call | the user opens the upload link in their own browser or adds the file in the Vivu web app |
| a listed file name does not match companies.csv | Vivu replaced spaces or punctuation with underscores | match by the NN prefix, or by length |
| "has not granted vivu.write" | Vivu connected read only | the user reconnects Vivu with write access |