NAI Camera Control
Turn two dials, stop hand-typing camera tags
- building_pipe
- context
- building_pipe
- prompt
- camera_prompt
What it's actually for
You've built a NovelAI prompt with character slots - a girl at 0.3, a girl at 0.7 - so you've solved where everyone stands. You haven't solved where the camera is. That's normally the part you fumble by hand: is it from below or from above, do you need dutch angle, and does adding one tag quietly break the framing words you already had?
NAI Camera Control is a dial box that writes those tags for you. Four axes, smooth blending between them, and out comes a caption string already formatted in NovelAI's weight syntax, appended to your scene prompt and - this is the useful bit - to every character caption too. It talks to your NAI context, so the whole thing stays one pipe into the generator.
It's the same engine as RS Camera Control under a different menu entry (RS_NovelAI_API → Camera); the one schema difference is that this one is NovelAI-syntax-first. Local pipeline? Use the other one.
How it works
No ControlNet, no camera model, no extra inference pass. It's arithmetic on text. Each axis is converted into tag weights by blending between knots with a raised cosine, s(t) = (1 − cos(πt)) / 2, so halfway between "front" and "side" you get two tags at roughly half weight each instead of a hard flip. Azimuth blends from front → viewed from the left side / viewed from the right side → from behind across 0°, 90°, 180°, scaled by 0.85 × cos(elevation)². That cosine is why ±90° elevation makes the horizontal caption vanish - at the pole, yaw stops meaning anything. Elevation goes from above / from below at 60°, overhead view / worm's-eye view at 90°. Distance interpolates across five framing tags. Roll adds dutch angle at 0.6 × |roll| / 45.
Then strength multiplies the lot and the result is emitted as 1.02::from behind:: (NovelAI) or (from behind:1.02) (ComfyUI), or as bare tags in plain mode. Weights under 0.005 get dropped, which is fine - the curves are smooth, the text isn't.
The honest caveat, straight from the author: this adds captions, not a camera. The numbers describe caption influence, not measured image angles. Side captions can turn each person independently rather than orbiting one shared camera.
The inputs that matter
You'll mostly touch four sliders and ignore the rest:
azimuth(−180…180, default 0) - horizontal view, draggable in the little diagram.elevation(−90…90) - positive looks down, negative looks up.distance(0…1, default 0.5) - 0 extreme close-up, 0.5 medium shot, 1 wide shot.strength(default 1.2) - start 0.8–1.4. Push it high and you get distorted or split images rather than a stronger camera.
Then scope decides whether the view caption gets appended to character captions as well (scene_and_characters, the default) or only to the common scene (scene_only). Leave it on the default. Regional setups can suppress a caption that only lives in the common prompt, which is exactly the failure mode you don't want to debug at 1 a.m.
weighting=plain drops the numbers for models that don't parse weighted prompts; prompt_syntax only matters when nothing is connected to building_pipe. random_mode (off/balanced/full) overrides the four axes with a seeded view; balanced stays inside −90…90 azimuth and −35…35 elevation, full uses everything. random_seed is camera-only - your image seed is untouched. Set its control to randomize if you want a new view per queue.
Outputs: building_pipe goes into the generator's nai_context (or the local sampler's character pipe), prompt is scene + camera as one string for a plain text path, and camera_prompt is just the captions - handy for debugging.
Install
ComfyUI Manager → Custom Nodes Manager → search ComfyUI_RS_NAI_API_Request → Install → restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/raspie10032/ComfyUI_RS_NAI_API_Request.git
cd ComfyUI_RS_NAI_API_Request
python -m pip install -r requirements.txt
Heads up: requirements.txt pulls torch, segment-anything and scipy for the pack's detailer and tagger. The camera node needs none of it, no model weights and no token - it never sends a request, so only the downstream generator spends Anlas.
Where people get burned
The missing-scene error. If the pipe you connect has no common prompt, the node refuses to run and asks for base_prompt rather than quietly replacing your scene with camera tags. That's a feature. Connect your scene text there.
Two cameras, or an existing view tag. Chaining cameras stacks captions, and entering a context that already says from behind gives you two views arguing with each other. Clean out the old view words first.
The model ignores you anyway. Prompt-only framing loses to conflicting text - looking at viewer, eye colour calls, an expression that faces the camera. People have chased a reliable back view for months. If a rear view won't take, simplify the scene and character captions before cranking strength.
NAI gets centers, not masks. Character positions are sent as coordinates; silhouettes only matter for local round trips. Don't expect a mask-based regional edit on the API side.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| azimuth | FLOAT | 0-180–180 | Horizontal view: 0 front, ±90 side, ±180 rear. / 좌우 시점: 0 정면, ±90 측면, ±180 뒤. |
| elevation | FLOAT | 0-90–90 | Positive looks down; negative looks up. This adds captions, not a 3D camera. / 양수는 내려다보기, 음수는 올려다보기입니다. 시점 태그를 추가합니다. |
| distance | FLOAT | 0.500–1 | 0 extreme close-up, 0.5 medium shot, 1 wide shot. / 0 극근접, 0.5 중간 거리, 1 원경. |
| roll | FLOAT | 0-45–45 | Tilt amount adds dutch angle. Text alone cannot guarantee tilt direction. / 기울기 양을 태그로 유도하며 방향까지 보장하지 않습니다. |
| strength | FLOAT | 1.200–5 | Caption weight scale. Start at 0.8–1.4; high values may distort or split the image. / 0.8~1.4부터 시작하세요. 높이면 왜곡·분할이 생길 수 있습니다. |
| weighting | COMBO | Plain omits numerical weights for models without weighted prompt parsing. / 가중치 문법을 지원하지 않는 경로에는 plain을 사용하세요. | |
| prompt_syntax | COMBO | Used without context. Connected context keeps its own syntax. / 컨텍스트가 없을 때 사용합니다. 연결 시 원본 문법을 따릅니다. | |
| enabled | BOOLEAN | true | Off passes the scene through without camera captions. / 끄면 시점 태그를 추가하지 않습니다. |
| scope | COMBO | Share the view with character regions, or affect only the common caption. Existing text regions and placements stay fixed. / 캐릭터 영역에도 같은 시점을 전달하거나 공통 문장에만 적용합니다. 글자 영역·배치는 유지됩니다. | |
| building_pipeopt | RS_MC_BUILD_PIPE,RS_MC_CHARACTER_PIPE,NAI_CONTEXT | Draft or final character pipe, or NAI context. In a builder, camera settings apply at Finalize regardless of role order. / 조립·최종 파이프 또는 NAI 컨텍스트. 조립 중 카메라는 순서와 관계없이 Finalize에서 적용됩니다. | |
| contextopt | RS_MC_BUILD_PIPE,RS_MC_CHARACTER_PIPE,NAI_CONTEXT | Legacy alias for building_pipe. / building_pipe의 이전 연결 이름입니다. | |
| base_promptopt | STRING | Optional scene caption, in the context's syntax. Required when a connected pipe has no scene. / 연결 파이프에 공통 프롬프트가 없으면 장면 문장을 연결하세요. | |
| random_modeopt | COMBO | off | Random view overrides the four manual axes. Balanced avoids rear/pole/extreme framing; full uses all ranges. / 랜덤 구도는 수동 각도·거리·기울기를 대신합니다. balanced는 극단 시점을 피하고 full은 전체 범위입니다. |
| random_seedopt | INT | 00–4294967295 | Camera seed only. Fixed reproduces the view; randomize changes it after each queued generation. / 카메라 전용 시드. fixed는 구도 유지, randomize는 생성마다 변경합니다. 이미지 생성 시드는 바꾸지 않습니다. |
| layout_syncopt | COMBO | fixed | perspective tilts the flat mask plane with view angles; distance_and_roll only zooms/rotates. Text stays fixed. / perspective는 좌우·상하 각도로 평면 마스크를 원근 변형합니다. distance_and_roll은 거리·회전만 연동. 글자는 고정. |
| canvas_aspectopt | FLOAT | 1.0000.1–10 | Output width divided by height. Use the sampler's ratio to avoid stretched mask rotation. / 생성 너비÷높이. 샘플러와 같은 비율로 맞추세요. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| building_pipe | RS_MC_BUILD_PIPE,RS_MC_CHARACTER_PIPE,NAI_CONTEXT | — |
| prompt | STRING | — |
| camera_prompt | STRING | — |