JoyCaption is an image captioning Visual Language Model (VLM) being built from the ground up as a free, open, and uncensored model for the community to use in training Diffusion models.
1,249
stars
21
commits
Jupyter Notebook
primary language
Feb 24, 2026
updated
JoyCaption is an open, free, and uncensored captioning Visual Language Model (VLM).
Try the Demo on HuggingFace | Download the Current Model on Hugging Face | Latest Release Post | Data

JoyCaption is an image captioning Visual Language Model (VLM) being built from the ground up as a free, open, and uncensored model for the community to use in training Diffusion models.
Key Features:
Automated descriptive captions enable the training and finetuning of diffusion models on a wider range of images, since trainers are no longer required to either find images with already associated text or write the descriptions themselves. They also improve the quality of generations produced by Text-to-Image models trained on them (ref: DALL-E 3 paper). But to-date, the community has been stuck with ChatGPT, which is expensive and heavily censored; or alternative models, like CogVLM, which are weaker than ChatGPT and have abysmal performance outside of the SFW domain.
I'm building JoyCaption to help fill this gap by performing near or on-par with GPT4o in captioning images, while being free, unrestricted, and open.
To see JoyCaption in action, check out the demo on HuggingFace Spaces.
To use JoyCaption locally, you can download the model from Hugging Face and integrate it into your existing workflows.
At its native datatype of bfloat16, JoyCaption needs about 17GB of VRAM for the model, so it runs comfortably on 24GB and up GPUs. If you need a lighterweight version, it can be quantized to 8-bit or 4-bit (https://github.com/fpgaminer/joycaption/issues/3#issuecomment-2870217672). The ComfyUI node (https://github.com/fpgaminer/joycaption_comfyui/) supports this natively.
import torch
from PIL import Image
from transformers import AutoProcessor, LlavaForConditionalGeneration
IMAGE_PATH = "image.jpg"
PROMPT = "Write a long descriptive caption for this image in a formal tone."
MODEL_NAME = "fancyfeast/llama-joycaption-beta-one-hf-llava"
# Load JoyCaption
# bfloat16 is the native dtype of the LLM used in JoyCaption (Llama 3.1)
# device_map=0 loads the model into the first GPU
processor = AutoProcessor.from_pretrained(MODEL_NAME)
llava_model = LlavaForConditionalGeneration.from_pretrained(MODEL_NAME, torch_dtype="bfloat16", device_map=0)
llava_model.eval()
with torch.no_grad():
# Load image
image = Image.open(IMAGE_PATH)
# Build the conversation
convo = [
{
"role": "system",
"content": "You are a helpful image captioner.",
},
{
"role": "user",
"content": PROMPT,
},
]
# Format the conversation
# WARNING: HF's handling of chat's on Llava models is very fragile. This specific combination of processor.apply_chat_template(), and processor() works
# but if using other combinations always inspect the final input_ids to ensure they are correct. Often times you will end up with multiple <bos> tokens
# if not careful, which can make the model perform poorly.
convo_string = processor.apply_chat_template(convo, tokenize = False, add_generation_prompt = True)
assert isinstance(convo_string, str)
# Process the inputs
inputs = processor(text=[convo_string], images=[image], return_tensors="pt").to('cuda')
inputs['pixel_values'] = inputs['pixel_values'].to(torch.bfloat16)
# Generate the captions
generate_ids = llava_model.generate(
**inputs,
max_new_tokens=512,
do_sample=True,
suppress_tokens=None,
use_cache=True,
temperature=0.6,
top_k=None,
top_p=0.9,
)[0]
# Trim off the prompt
generate_ids = generate_ids[inputs['input_ids'].shape[1]:]
# Decode the caption
caption = processor.tokenizer.decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)
caption = caption.strip()
print(caption)
JoyCaption Beta One offers multiple modes of caption generation to suit different needs. Descriptive Caption and Straightforward are the most useful, with the other modes being interesting but a little less stable. The HuggingFace demo has a nice interface for selecting the output mode and extra options, and it outputs the prompt it used. Otherwise, here are all the prompts that JoyCaption Beta One understands:
Descriptive Caption: Writes descriptive captions for the image, either in a formal or casual tone.
Straightforward Caption: A more concise, objective style than Descriptive.
Stable Diffusion Prompt: Tries to mimic how users typically write Stable Diffusion prompts, with a mixture of natural language and booru-like tags.
MidJourney: Similar to Training Prompt mode but more like MidJourney prompts.
Danbooru tag list: Writes a list of Danbooru tags for the image.
artist:, copyright:, character:, meta:, then general tags. Include counts (1girl), appearance, clothing, accessories, pose, expression, actions, background. Use precise Danbooru syntax. No extra text."artist:, copyright:, character:, meta:, then general tags. Include counts (1girl), appearance, clothing, accessories, pose, expression, actions, background. Use precise Danbooru syntax. No extra text. {word_count} words or less."artist:, copyright:, character:, meta:, then general tags. Include counts (1girl), appearance, clothing, accessories, pose, expression, actions, background. Use precise Danbooru syntax. No extra text. {length} length."e621 tag list: Writes a list of e621 tags for the image.
Rule34 tag list: Writes a list of Rule34 tags for the image.
Booru-Like Tag List: Similar to Booru Tag List mode, but will write outside the strict list of tags that boorus use.
Art Critic Analysis: Writes an analysis of the image like an art critic.
Product Listing: Writes a product listing-style caption for the image.
The following extra instructions can be appended to the prompt to guide the caption generation:
WARNING: Beta One is not a general instruction follower. Feel free to experiment outside of these prompts, but don't expect perfect adherence.
vLLM provides the highest performance inference for JoyCaption, and an OpenAI compatible API so JoyCaption can be used like any other VLMs. Example usage:
vllm serve fancyfeast/llama-joycaption-beta-one-hf-llava --max-model-len 4096 --enable-prefix-caching
VLMs are a bit finicky on vLLM, and vLLM is memory hungry, so you may have to adjust settings for your particular environment, such as forcing eager mode, adjusting max-model-len, adjusting gpu_memory_utilization, etc.
On Windows the easiest way to use vLLM is to setup docker and run something like:
docker run --gpus all --ipc=host -p 8000:8000 -v "%USERPROFILE%\.cache\huggingface:/root/.cache/huggingface" vllm/vllm-openai:latest --model fancyfeast/llama-joycaption-beta-one-hf-llava --max-model-len 4096 --enable-prefix-caching
-v "%USERPROFILE%\.cache\huggingface:/root/.cache/huggingface" persists the model cache outside of docker, so it doesn't have to re-download the model every time you run the container.
Finetuning scripts and documentation can be found in the finetuning directory. The finetuning/README.md file contains detailed instructions on how to prepare your data and train JoyCaption on it.
JoyCaption is currently at Beta One. This means that things are nearing completion for version 1.0.
Please note that JoyCaption, like all VLMs, is not perfect. Expect issues when it comes to multiple subjects, left/right confusion, OCR inaccuracy, etc. Instruction following is better than Alpha Two, but will occasionally fail and is not as robust as a fully fledged SOTA VLM. And though I've drastically reduced the incidence of glitches, they do still occur 1.5 to 3% of the time. As an independent developer, I'm limited in how far I can push things. For comparison, commercial models like GPT4o have a glitch rate of 0.01%.
If you use Beta One as a more general purpose VLM, asking it questions and such, on NSFW queries you may find that it occasionally responds with a refusal. This is not intentional, and Beta One itself was not censored. However certain queries can trigger llama's old safety behavior. Simply re-try the question, phrase it differently, or tweak the system prompt to get around this.
Feedback is always welcome and crucial to helping me improve JoyCaption for everyone to use! If you have suggestions for improvement, notice weaknesses, or want to contribute to the project, please reach out.
20 commits
1 commits
Jupyter Notebook
74.1%
Python
25.9%
JoyCaption is an image captioning Visual Language Model (VLM) being built from the ground up as a free, open, and uncensored model for the community to use in training Diffusion models.
1,249
stars
21
commits
Jupyter Notebook
primary language
Feb 24, 2026
updated
JoyCaption is an open, free, and uncensored captioning Visual Language Model (VLM).
Try the Demo on HuggingFace | Download the Current Model on Hugging Face | Latest Release Post | Data

JoyCaption is an image captioning Visual Language Model (VLM) being built from the ground up as a free, open, and uncensored model for the community to use in training Diffusion models.
Key Features:
Automated descriptive captions enable the training and finetuning of diffusion models on a wider range of images, since trainers are no longer required to either find images with already associated text or write the descriptions themselves. They also improve the quality of generations produced by Text-to-Image models trained on them (ref: DALL-E 3 paper). But to-date, the community has been stuck with ChatGPT, which is expensive and heavily censored; or alternative models, like CogVLM, which are weaker than ChatGPT and have abysmal performance outside of the SFW domain.
I'm building JoyCaption to help fill this gap by performing near or on-par with GPT4o in captioning images, while being free, unrestricted, and open.
To see JoyCaption in action, check out the demo on HuggingFace Spaces.
To use JoyCaption locally, you can download the model from Hugging Face and integrate it into your existing workflows.
At its native datatype of bfloat16, JoyCaption needs about 17GB of VRAM for the model, so it runs comfortably on 24GB and up GPUs. If you need a lighterweight version, it can be quantized to 8-bit or 4-bit (https://github.com/fpgaminer/joycaption/issues/3#issuecomment-2870217672). The ComfyUI node (https://github.com/fpgaminer/joycaption_comfyui/) supports this natively.
import torch
from PIL import Image
from transformers import AutoProcessor, LlavaForConditionalGeneration
IMAGE_PATH = "image.jpg"
PROMPT = "Write a long descriptive caption for this image in a formal tone."
MODEL_NAME = "fancyfeast/llama-joycaption-beta-one-hf-llava"
# Load JoyCaption
# bfloat16 is the native dtype of the LLM used in JoyCaption (Llama 3.1)
# device_map=0 loads the model into the first GPU
processor = AutoProcessor.from_pretrained(MODEL_NAME)
llava_model = LlavaForConditionalGeneration.from_pretrained(MODEL_NAME, torch_dtype="bfloat16", device_map=0)
llava_model.eval()
with torch.no_grad():
# Load image
image = Image.open(IMAGE_PATH)
# Build the conversation
convo = [
{
"role": "system",
"content": "You are a helpful image captioner.",
},
{
"role": "user",
"content": PROMPT,
},
]
# Format the conversation
# WARNING: HF's handling of chat's on Llava models is very fragile. This specific combination of processor.apply_chat_template(), and processor() works
# but if using other combinations always inspect the final input_ids to ensure they are correct. Often times you will end up with multiple <bos> tokens
# if not careful, which can make the model perform poorly.
convo_string = processor.apply_chat_template(convo, tokenize = False, add_generation_prompt = True)
assert isinstance(convo_string, str)
# Process the inputs
inputs = processor(text=[convo_string], images=[image], return_tensors="pt").to('cuda')
inputs['pixel_values'] = inputs['pixel_values'].to(torch.bfloat16)
# Generate the captions
generate_ids = llava_model.generate(
**inputs,
max_new_tokens=512,
do_sample=True,
suppress_tokens=None,
use_cache=True,
temperature=0.6,
top_k=None,
top_p=0.9,
)[0]
# Trim off the prompt
generate_ids = generate_ids[inputs['input_ids'].shape[1]:]
# Decode the caption
caption = processor.tokenizer.decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)
caption = caption.strip()
print(caption)
JoyCaption Beta One offers multiple modes of caption generation to suit different needs. Descriptive Caption and Straightforward are the most useful, with the other modes being interesting but a little less stable. The HuggingFace demo has a nice interface for selecting the output mode and extra options, and it outputs the prompt it used. Otherwise, here are all the prompts that JoyCaption Beta One understands:
Descriptive Caption: Writes descriptive captions for the image, either in a formal or casual tone.
Straightforward Caption: A more concise, objective style than Descriptive.
Stable Diffusion Prompt: Tries to mimic how users typically write Stable Diffusion prompts, with a mixture of natural language and booru-like tags.
MidJourney: Similar to Training Prompt mode but more like MidJourney prompts.
Danbooru tag list: Writes a list of Danbooru tags for the image.
artist:, copyright:, character:, meta:, then general tags. Include counts (1girl), appearance, clothing, accessories, pose, expression, actions, background. Use precise Danbooru syntax. No extra text."artist:, copyright:, character:, meta:, then general tags. Include counts (1girl), appearance, clothing, accessories, pose, expression, actions, background. Use precise Danbooru syntax. No extra text. {word_count} words or less."artist:, copyright:, character:, meta:, then general tags. Include counts (1girl), appearance, clothing, accessories, pose, expression, actions, background. Use precise Danbooru syntax. No extra text. {length} length."e621 tag list: Writes a list of e621 tags for the image.
Rule34 tag list: Writes a list of Rule34 tags for the image.
Booru-Like Tag List: Similar to Booru Tag List mode, but will write outside the strict list of tags that boorus use.
Art Critic Analysis: Writes an analysis of the image like an art critic.
Product Listing: Writes a product listing-style caption for the image.
The following extra instructions can be appended to the prompt to guide the caption generation:
WARNING: Beta One is not a general instruction follower. Feel free to experiment outside of these prompts, but don't expect perfect adherence.
vLLM provides the highest performance inference for JoyCaption, and an OpenAI compatible API so JoyCaption can be used like any other VLMs. Example usage:
vllm serve fancyfeast/llama-joycaption-beta-one-hf-llava --max-model-len 4096 --enable-prefix-caching
VLMs are a bit finicky on vLLM, and vLLM is memory hungry, so you may have to adjust settings for your particular environment, such as forcing eager mode, adjusting max-model-len, adjusting gpu_memory_utilization, etc.
On Windows the easiest way to use vLLM is to setup docker and run something like:
docker run --gpus all --ipc=host -p 8000:8000 -v "%USERPROFILE%\.cache\huggingface:/root/.cache/huggingface" vllm/vllm-openai:latest --model fancyfeast/llama-joycaption-beta-one-hf-llava --max-model-len 4096 --enable-prefix-caching
-v "%USERPROFILE%\.cache\huggingface:/root/.cache/huggingface" persists the model cache outside of docker, so it doesn't have to re-download the model every time you run the container.
Finetuning scripts and documentation can be found in the finetuning directory. The finetuning/README.md file contains detailed instructions on how to prepare your data and train JoyCaption on it.
JoyCaption is currently at Beta One. This means that things are nearing completion for version 1.0.
Please note that JoyCaption, like all VLMs, is not perfect. Expect issues when it comes to multiple subjects, left/right confusion, OCR inaccuracy, etc. Instruction following is better than Alpha Two, but will occasionally fail and is not as robust as a fully fledged SOTA VLM. And though I've drastically reduced the incidence of glitches, they do still occur 1.5 to 3% of the time. As an independent developer, I'm limited in how far I can push things. For comparison, commercial models like GPT4o have a glitch rate of 0.01%.
If you use Beta One as a more general purpose VLM, asking it questions and such, on NSFW queries you may find that it occasionally responds with a refusal. This is not intentional, and Beta One itself was not censored. However certain queries can trigger llama's old safety behavior. Simply re-try the question, phrase it differently, or tweak the system prompt to get around this.
Feedback is always welcome and crucial to helping me improve JoyCaption for everyone to use! If you have suggestions for improvement, notice weaknesses, or want to contribute to the project, please reach out.
20 commits
1 commits
Jupyter Notebook
74.1%
Python
25.9%