Tutorial: Vibe transfer¶
Encode a reference image’s style or identity into a vibe token, then pass it to a generation. Vibe transfer (V4+ only) is the cleanest way to reproduce a style or character across multiple generations.
When to use vibe transfer¶
1. Encode a reference¶
import base64
from pathlib import Path
ref_b64 = base64.b64encode(Path("reference.png").read_bytes()).decode("ascii")
# Style-focused vibe: low information_extracted
style_vibe = await ctx.session.call_tool("encode_vibe", {
"reference": ref_b64,
"information_extracted": 0.3,
})
# Returns a base64 vibe token (string)
2. Use the vibe in a generation¶
result = await ctx.session.call_tool("generate_image", {
"prompt": "1girl, sitting in a cafe, masterpiece",
"references": [style_vibe],
})
The generated image inherits the reference’s painterly style, lighting, and palette — but uses a completely different subject and composition.
3. Blend multiple vibes¶
references accepts a list. Blend a style vibe and a character vibe:
# Style vibe — captures the painterly look
style_vibe = await ctx.session.call_tool("encode_vibe", {
"reference": style_ref_b64,
"information_extracted": 0.2,
})
# Character vibe — captures identity
character_vibe = await ctx.session.call_tool("encode_vibe", {
"reference": character_ref_b64,
"information_extracted": 0.9,
})
result = await ctx.session.call_tool("generate_image", {
"prompt": "1girl, sitting in a cafe",
"references": [style_vibe, character_vibe],
})
The result has the character’s face/identity in the style’s painterly look.
4. Tune information_extracted¶
Value |
Effect |
|---|---|
|
Style transfer — lighting, palette, brushwork. Identity not preserved. |
|
Balanced — style + approximate character archetype. |
|
Identity transfer — same character in a new pose/scene. |
Tip
Start at 0.3 for style transfer and 0.9 for identity transfer. Iterate
in steps of 0.1.
5. Caching¶
The server caches vibe encodings in-memory (size:
NOVELAI_VIBE_CACHE_ENTRIES, default 64). Encoding the same reference with
the same parameters a second time hits the cache and returns instantly
without an API call.
# First call: hits the API (~2 seconds)
vibe1 = await ctx.session.call_tool("encode_vibe", {
"reference": ref_b64,
"information_extracted": 0.3,
})
# Second call with the same args: cache hit, returns instantly
vibe2 = await ctx.session.call_tool("encode_vibe", {
"reference": ref_b64,
"information_extracted": 0.3,
})
# vibe1 == vibe2
6. Vibe transfer only¶
generate_image accepts vibe references but not ControlNet conditions. To
combine a structural map with a style, extract the annotation with
annotate_image and apply it via director_tool for now; ControlNet
conditioning on generate_image is not implemented yet.
# Step 1: extract depth from a pose reference (saved to disk)
depth = await ctx.session.call_tool("annotate_image", {
"image": pose_photo_b64,
"model": "midas",
})
# Step 2: encode a style vibe from a painting reference
style_vibe = await ctx.session.call_tool("encode_vibe", {
"reference": painting_ref_b64,
"information_extracted": 0.2,
})
# Step 3: generate with the vibe (references are supported, controlnet is not)
result = await ctx.session.call_tool("generate_image", {
"prompt": "1girl, standing in a meadow",
"references": [style_vibe],
})
The result carries the painting’s style; structural conditioning on
generate_image is not implemented yet.
7. Pitfalls¶
Warning
V5 models do not support vibes. encode_vibe with a V5 model id raises a
ToolError. V3 / Furry models are outside the supported set as well, but the
rejection is enforced by the API rather than locally — check with
supports_vibe() (takes a Model enum) instead of relying on an error:
from novelai_image_mcp.nai import supports_vibe, Model
supports_vibe(Model.V4_5) # True
supports_vibe(Model.V3) # False
supports_vibe(Model.V5) # False
Tip
Reference quality matters. A single high-quality, well-cropped reference (close-up, neutral background) produces better vibes than a noisy album of similar images.
What’s next?¶
ControlNet tutorial — combine vibes with structural conditioning