FrameThrower Reference Node
Search 410,000 film stills without leaving your graph — the FrameThrower reference node
- image
- depth
- pose
- lineart
- prompt (text)
- credit (text)
Between "scrape a movie screenshot and hope nobody notices" and "spend an hour on a stock site" there's a better way to get a reference frame: search a curated film-still library from inside your graph. That's what the FrameThrower Reference Node does. Type a description, get a grid of stills from 5,490 films (roughly 410,000 of them), click the one whose lighting or composition you want, and out comes the frame, its scene description, its credit line, and - if you ask - depth, pose and lineart. It's a moodboard you can wire into a ControlNet.
The pack is basically this one node. It's the ComfyUI door into framethrower.ai, a film-still search app. Don't confuse it with an API-wrapper generator: it's not calling a closed model. It searches a library, and the parts that touch your generation - depth, pose, lineart - run on your own machine. No key in a widget, no phone-home for inference.
How it works
The node carries its own little web UI - a search box and results grid inside the node body. Click a frame and it gets "pinned": that exact frame is what executes, because the search is not re-run at queue time - a queued graph can't quietly swap in a different picture than the one you clicked.
Two ways to drive it, and they behave differently on purpose. Click a frame and it pins - deterministic, what-you-see-is-what-executes. Or wire a string into query_in and set index: nothing is pinned, the node searches at execution time and takes result n. That's the batch and automation mode - query_in is "automatic," the grid follows the wire live as you type upstream. The auto widget is grid bookkeeping, not for typing.
The inputs and outputs that matter
A beginner actually sets maybe three. query (the big multiline one) is what you search for - "neon rain at night" is the placeholder, and a good example. mode defaults to hybrid (text + visual meaning), nearly always the right answer; description matches text metadata only. index picks which result executes (0–499), mostly relevant when you're driving from query_in. The rest - pinned, filters, smart, auto, lineart_strength - the node's own UI writes for you.
The outputs are where it gets fun. image is the frame, straight into a VAE Encode, an IPAdapter, or a ControlNet. Then depth, pose, lineart - and here's the part people miss: those three only run if you wire them. The node inspects the executing graph, sees nothing reading the socket, and skips the processor; unwired, a socket outputs a single black pixel rather than nothing (Comfy has no null on an IMAGE socket). Depth is Depth-Anything-V2-Small via the transformers pipeline ComfyUI already ships; pose is a tiny detector (yolos-tiny) finding people plus ViTPose on the joints, rendered in OpenPose's layout so pose ControlNets know what to do with it; lineart is a gradient-magnitude pass with a lineart_strength dial (default 3.0) controlling how much detail survives. All local, all one-time weight downloads - the README claims depth in ~65ms against nearly a minute on a cold cloud model.
The two text outputs are easy to trip over. prompt (text) is the frame's scene description as plain text - wire it into CLIP Text Encode and it becomes conditioning. credit (text) is the Blade Runner (1982) - dir. Ridley Scott line. To see them, wire into the node called Preview as Text (its internal id is PreviewAny - search the menu for the display name), or Save Text to write them beside the picture. Don't wire into a Primitive String node - it makes text rather than showing it, so the wire can't even be made.
Install
ComfyUI Manager: search FrameThrower, Install, restart. Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/framethrower-ai/comfyui-framethrower
Restart, then Add Node → FrameThrower → Reference Node. Needs ComfyUI 0.3.30 or newer. No pip step - dependencies = [] in the pyproject, because torch/transformers/PIL all ship with ComfyUI. The only downloads are the preprocessor weights on first use.
There's no key to copy. Click Connect on the node, it shows a short code, you approve it at framethrower.ai/link, and the token lands in ComfyUI/user/framethrower/config.json (mode 600). It's deliberately not a widget: widget values serialize into the workflow .json, so a shared workflow would carry your key around. Headless or shared box? Set FT_API_TOKEN in the environment instead; it overrides the file.
Gotchas
- Node draws as a stack of plain widgets? Your frontend is older than 1.16 - update ComfyUI.
- "Not connected to FrameThrower" on execute. The Connect flow never finished, or
FT_API_TOKENis stale. Check that config.json. - Connect says it can't write. Rare -
ComfyUI/user/is read-only. UseFT_API_TOKEN. - It costs credits. Searches are metered against your framethrower.ai balance (signup gives you credits, no card). But the node hashes its inputs via
IS_CHANGED, so re-queueing an unchanged graph is a cache hit and spends nothing - leave the node in a workflow and iterate the sampler side forever without burning balance.
If you already have a solid depth/pose pipeline, ignore three of the image sockets entirely - grab the frame and the description, and go steal some lighting.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| query | STRING | — | |
| mode | COMBO | hybrid | 2 options: hybrid, description |
| index | INT | 00–499 | — |
| pinned | STRING | — | |
| filters | STRING | — | |
| smart | BOOLEAN | true | — |
| auto | STRING | — | |
| lineart_strength | STRING | 3.0 | — |
| query_inopt | STRING | Words to search for, from another node. Automatic — the grid follows the wire as you type upstream, no queue needed. |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | The frame itself. Into Preview Image, Save Image, or a VAE Encode / ControlNet / IPAdapter that takes an IMAGE. |
| depth | IMAGE | Depth map. Into a depth ControlNet. Runs only while this socket is wired. |
| pose | IMAGE | Pose skeleton, OpenPose layout. Into a pose ControlNet. Runs only while this socket is wired, and is empty when nobody is in the frame. |
| lineart | IMAGE | Lineart. Into a lineart ControlNet. Runs only while this socket is wired. |
| prompt (text) | STRING | The frame's scene description, as plain text — not conditioning. Into CLIP Text Encode, which turns it into conditioning. To read it on the canvas, add the node called 'Preview as Text'. Not a Text node: that one makes text rather than showing it. |
| credit (text) | STRING | Title, year and director, as text. Into 'Preview as Text' to read it, or 'Save Text' to write it beside the picture, so the attribution travels with whatever you make. |