openjutsu README

The project's README from github.com/D3voz/openjutsu. As one markdown file: README.md.

Openjutsu

An open-source, Genjutsu-style character and object swap for video. Drop a video and a character photo, press Generate, and the main person in the video is replaced by your character. Or change their hair or clothes, remove something, or put them somewhere else. The camera, cuts, motion and soundtrack stay. Everything runs on open weights on your own GPU: MiniMax H3 for the video, SAM 3.1 to track people, and Qwen3-VL 8B to describe the pictures and shots.

The app is a small Next.js front end over a ComfyUI workflow. It uses @comfyorg/sdk, following Comfy's comfy-reshoot reference app.

https://github.com/user-attachments/assets/2d8d763a-1764-49a6-9116-d6d1be661ff6

Advanced: several people, pick who becomes who Object swap: paint where it goes
Two people swapped, each from their own photo Sunglasses painted onto a person's eyes
From picture: a new look and place from one picture Post-processing: fix faces
A video frame and a picture combined into a posed picture, then a video A small face redrawn at close-up size

From picture → Relight: new light in the same pass

A video frame and a picture combined into a posed picture under golden backlight, then a video that keeps the light, with the seventeen light looks below

First visit: visual describer

The welcome page lets you choose the vision-language model that describes pictures and video frames and writes prompts. Qwen3-VL 8B INT8 remains the default; Qwen3-VL 4B FP8 and Gemma 4 E2B INT8 are smaller alternatives. Install a compatible single-file model including its vision weights in ComfyUI/models/text_encoders, then press Refresh models. Other installed models can be selected if they support image/video input and text generation through ComfyUI's CLIPLoader and Generate Text nodes. Text-only models and files requiring a different loader are unsuitable.

Choose according to available VRAM and leave room for image/video context and working memory; file size is not a VRAM requirement, and H3 generation has separate memory needs. Your selection is saved in this browser and used for swaps, prompt descriptions and Find people. Reopen Welcome & models to change it, or use Visual describer under Model components. Alternative describers may change prompt quality and person matching. Set up later opens the editor; generation still checks the required files. The separate H3 text encoder remains unchanged.

Modes

Requirements

Custom nodes

Install these into ComfyUI/custom_nodes (ComfyUI Manager finds the first three by name):

Pack Used for
SolarRenderer From picture: SolarRenderer LoRA loading, conditioning and four-step sampling
ComfyUI-MiniMaxH3-Context-Loop 0.7.3+ clip loop, checkpoints, final assembly
ComfyUI-Fantastic-MiniMaxH3-PromptBuilder masked video-edit latent
ComfyUI-KJNodes Grow Mask With Blur; From picture: VRAM cleanup
comfyui_controlnet_aux From picture: Metric3D normal maps
ComfyUI-VideoHelperSuite video loading
comfyui-h3-oneclick-swap swap and object plans, people and role masks, grey mannequins, track labelling, cut finder, shot list, face-fix chunks
ComfyUI-H3-FaceRefine Fix faces: face tracking and cropping, SAM face masks, pasting back
ComfyUI-Impact-Pack Fix faces: SAM loader
comfyui-image-detail-contrast Fix colours: Image Detail Contrast
Orion4D_FXMax Fix colours: DCTL Tone Mapper and its presets
comfyui-websocket-reconnect-fix every job: keeps live progress coming after the proxy reconnects to ComfyUI (adds no nodes)

Restart ComfyUI after installing them.

Before uploading a job, Openjutsu checks the selected workflow against ComfyUI's node and model lists. Missing packs appear with installation links; missing model files appear with their destination folders. Disabled steps are excluded from the check. This checks registered nodes and listed model files, not Python dependencies, model integrity or available VRAM. COMFYUI_URL must point at ComfyUI itself (normally http://127.0.0.1:8188).

Models

For From picture → SolarRenderer, download solarrenderer_4step_lora.safetensors and solarrenderer_control_lora.safetensors from Enderfga/SolarRenderer, or the corresponding rank-64 pair from mdevoz/SolarRendererRank64. Put both in ComfyUI/models/loras or a subfolder, reload the app and choose their paths in the render settings.

Folder in ComfyUI/models File
diffusion_models minimax_h3_ref2va_pruned_int8_convrot.safetensors
loras minimax_h3_DMAD_4step_full_lora_avg_rank_39_bf16.safetensors
text_encoders qwen3vl_32b_minimax_h3_int4_convrot.safetensors (H3 text encoder)
text_encoders qwen3vl_8b_int8_convrot.safetensors (describer)
vae minimax_h3_video_vae_int8_convrot.safetensors, minimax_h3_audio_vae_fp32.safetensors
checkpoints sam3.1_multiplex_fp16.safetensors
ultralytics/bbox, ultralytics/segm face_yolov8m.pt, person_yolov8m-seg.pt (Bingsu/adetailer), for Fix faces
sams sam_vit_b_01ec64.pth, for Fix faces
insightface/models buffalo_l (InsightFace downloads it on first use), for Fix faces

Run it locally

On Windows, double-click start_openjutsu.bat (ComfyUI must be running, or started while it waits). The first run installs comfy-api-proxy 0.1.6 into .venv-proxy and the node packages, and patch_proxy.py fixes the proxy's live progress (node changes and mid-job previews). Each run builds the app, then opens the proxy and the app (npm start, the production server) in two windows and the page in your browser. Close those two windows to stop Openjutsu. The manual steps:

  1. Start ComfyUI on 127.0.0.1:8188. On Windows, set AIOHTTP_NOSENDFILE=1 first (PowerShell: $env:AIOHTTP_NOSENDFILE = "1"). Without it, the first partial-file request (the video player sends one when you seek) can stall ComfyUI's /view, and every later file download hangs until ComfyUI restarts.

  2. Install the proxy, fix its live progress with this repo's patch_proxy.py (run it with the same Python), and start it. It serves the Comfy API v2 on 127.0.0.1:8189. Raise the upload cap for longer videos (the default is 100 MB):

    pip install comfy-api-proxy==0.1.6
    python openjutsu/patch_proxy.py
    comfy-api-proxy run --max-upload-mb 500
    
  3. Install and start the app:

    git clone <this repo> openjutsu
    cd openjutsu
    cp .env.example .env.local
    npm ci
    npm run build
    npm start
    

    While changing the code, npm run dev runs the development server instead.

  4. Open http://127.0.0.1:3000.

.env.local sets COMFY_BASE_URL=http://127.0.0.1:8189. Leave COMFY_API_KEY empty for a local proxy. The base model and LoRA pickers read ComfyUI's model lists from COMFYUI_URL (default http://127.0.0.1:8188); without it they become text fields.

Uploads are limited to 512 MB total per request, including reference pictures. Uploads are streamed to a temporary folder, and up to three can arrive at once per app process; a fourth is asked to retry shortly. After a job ends, its uploads are removed from comfy-api-proxy, but the files stay in ComfyUI's input/ folder. Generation continues in ComfyUI independently.

A 6 s clip at 480p takes about 6–13 minutes on a 4060 Ti, most of it in H3 sampling. 736p is the default; 1080p is about 2.5× slower than 736p and needs much more memory. Custom sets an exact width × height from 256 to 2048 px a side (rounded to 32); the video is scaled and centre-cropped to that shape first. It needs the short_edge input of the H3 Swap Plan nodes in comfyui-h3-oneclick-swap.

Advanced motion transfer has a Montage tab for edited clips where different people take turns within one shot without a hard cut (whip pans, selfie handovers, travel montages). Write roles as "who - looks" ("the dancer - white polo, jeans"): the looks help match the people, and only the part before " - " reaches H3. Generate tracks everyone with SAM over the whole video, matches each appearance to the roles with Qwen (a person SAM loses and finds again is matched again, so they keep their photo; Qwen sees each appearance in its frame and close up at three moments, a few appearances at a time, and is asked again about any it skips or matches to one photo twice at the same moment) and makes the video as one chain like Standard, with overlapping clips: each clip has one H3 pass per photo on screen in it (the two with the most screen time) that masks only that photo's appearances, with a prompt about that person only. Appearances on screen for under 12 frames or at under 2% of the frame stay as they are, since H3 draws them as grey figures; the result page lists them. Find people (about 3 minutes for 18 s of video) is the optional review: it does the tracking and matching alone, lists the appearances with a photo picker each and saves the tracks, and Generate from that list masks with exactly those tracks. The saved tracks live in ComfyUI's temp folder and are checked against the video, part and size, so after a ComfyUI restart or a change, Find people again. It needs the pack's H3TrackCrops and H3TrackRoles (which now runs Qwen itself), H3MontagePeople, H3MontageTracks, H3MontagePicks, H3MontagePlan and H3MontagePass nodes.

Tips

How it works

Each clip, SAM 3.1 tracks everyone at 1008 px (its own working size), and H3 People Mask keeps the largest person. The edit mask adds 10% headroom above the head, holds each pose for 2 frames and grows by 40 px, so a differently built character has room; it does not affect the motion. The motion reference H3 follows is the source clip with that person turned into a sharp grey mannequin of their exact silhouette, so the pose, arms and fingers stay readable.

workflows/simple.api.json is one API export of that workflow with the hat fix and No lip-sync enabled. For each request, lib/simple.ts:

lib/model.ts then sets the Swap Plan's steps and the H3 Diffusion Model file, and replaces the DMAD LoRA node with the chosen LoRAs in a chain (or links the model straight through when there are none). Advanced mode does the same.

lib/comfy.ts binds the uploaded video to VHS_LoadVideo and the photo to the LoadImage node, then submits with the SDK. /api/jobs/[id]/events reads the job's raw event stream (the SDK's low-level getJobEvents) and forwards it to the page as server-sent events. Node ids are turned into node titles and clip numbers there: each clip after the first is a graph expansion of the loop, whose node ids carry a prefix per clip (64, 66.0.0.64, 66.0.0.Recurse.0.0.64 for Save Clip in clips 1–3). The final MP4 is the output of the Final Video node (MiniMaxH3ChainAssemble, node 67).

workflows/mask.api.json is the mask check: the same SAM, People Mask and grow settings over the whole video, with the mask tinted over the frames next to the mannequin, saved as an MP4.

Advanced mode labels the people before it masks them. Per clip, H3 Track Crops shows each SAM track once per shot (the person alone, and the frame with them boxed) to Qwen3-VL 8B, which names the subject or "other"; H3 Track Roles reads that answer and settles a subject named twice in one shot by colour. H3 Role Mask builds the edit mask from the labelled tracks only, and the shot-list describer sees each labelled person outlined in their subject's colour, so the prompt names the same people the masks do. lib/advanced.ts finds nodes by title, drops unused picture branches and, without the hat fix or No lip-sync, their nodes; the exports are made from workflows/build/oneclick_advanced_build.js with every optional group on.

Object swap uses the same masked edit for the tracked things; SAM 3 tracks each comma-separated thing as its own prompt. The H3 Object Swap Plan writes a MiniMax video-editing prompt ([video editing + reference generation + audio reuse]), and the shot-list describer is told not to mention the thing being changed. Change: the tracked area is regenerated, and the motion reference shows it as a grey shape that keeps its shape and movement but not its colours. Background: the grown person mask is inverted (flip_input), so the edit reaches a little into the person's edge, and the whole background turns into a grey blur that keeps the camera movement. Remove: there is no motion reference (a grey shape would come back as a grey object), and the prompt describes what fills the area without naming what was there. lib/object.ts makes these changes by node title.

Fix faces works on the face, not the frame. H3 Face Track + Crop follows the face of the person in the photo over the whole video (InsightFace picks them out when there are several) and crops it to 512×512, so a face a few dozen pixels tall fills the canvas. H3 Face Fix Chunks splits the crops into independent chunks (6.5 s by default, overlapping by at least 1 s) and outputs them as lists, so ComfyUI runs the H3 nodes once per chunk. Each chunk is a masked edit: only the SAM face mask is regenerated with the photo as reference, while the hair, ears, collar and background around it stay, which keeps the new face from sliding against the head. For Follow the mouth, a blurred copy of the crop is the motion reference. H3 Face Fix Join crossfades the chunk overlaps and lowers the per-frame paste weight where the face is large, and H3 Face Stitch pastes the face back through a slightly smaller mask with the original soundtrack. The chunks are not chained through the context loop: each one starts from the swap's own frames, so colours do not drift. lib/face.ts removes the blurred face for Match the photo and sets the seed and chunk length.

The swap workflows are built by the scripts in workflows/build/: oneclick_single_build.js (Simple), oneclick_advanced_build.js (Advanced; set globalThis.H3_MAX_QUALITY = true first for One pass per person), and oneclick_object_build.js. To rebuild one, open ComfyUI in the browser with an empty workflow tab (each script clears the active tab), run const app = window.comfyAPI.app.app; in the developer console, paste the script, enable its optional groups (hat fix, No lip-sync) where the export needs them, export Workflow (API) and replace the matching workflows/*.api.json. Fix faces is written by python workflows/build/openjutsu_face_build.py. If node ids change, update NODES in lib/simple.ts. npm test checks the pruning against the exports.

Deploy on the Comfy Developer Platform

Not tested yet. The app code does not change, only .env.local:

  1. Package the prepared ComfyUI install (with the custom nodes and models above) as a Build, and deploy it:

    pip install comfy-cli
    comfy setup --where cloud
    cd /path/to/ComfyUI
    comfy build init --name openjutsu --models-dir ./models --custom-nodes-dir ./custom_nodes
    comfy build status
    comfy build push --release --target linux/nvidia
    comfy deploy refs compute
    
  2. Create a deployment from the released Build on the Developer Platform. The workflow has only been run on a 16 GB card with 64 GB of system RAM, so choose at least that.

  3. Set COMFY_BASE_URL to the deployment URL and COMFY_API_KEY to a key from the workspace that owns it. The key stays on the server; never put it in a NEXT_PUBLIC_ variable.

  4. Before hosting or tunneling the app, set a strong OPENJUTSU_PASSWORD and use HTTPS. Without a password, Openjutsu refuses to start on any address but loopback. It also refuses a password shorter than 12 characters. After 5 wrong passwords a client must wait before each try, from 1 second doubling to 5 minutes; clients are told apart by their X-Forwarded-For address, which only a proxy or tunnel that sets it makes reliable, so 20 failures spread across clients make everyone wait too. Open the page and sign in as openjutsu with that password; the same browser login protects uploads, job controls, progress and downloads. This is a single-owner app: everyone given the password shares access to its jobs. The start-up check cannot see a tunnel or reverse proxy on the same machine, so never point one at a password-free server.

  5. Host the Next.js app anywhere that runs Node. npm start listens on 127.0.0.1 only; to serve on a network address, build and start Next with the password set (Next uses PORT if the host sets it, otherwise 3000):

    npm run build
    OPENJUTSU_PASSWORD='<strong password>' npx next start --hostname 0.0.0.0
    

    Streaming progress needs a host that keeps long responses open. Preserve the public Host and scheme when reverse proxying so same-origin API checks work.

Notes:

License

MIT, see LICENSE. Model licences are separate. Fix faces depends on InsightFace buffalo_l, which is licensed for non-commercial research only, and on the Ultralytics YOLO weights from Bingsu/adetailer, which are AGPL-3.0.

openjutsu (D3voz/openjutsu)

Searching Database…

One-click character swap for videos: an open-source, local Higgsfield Genjutsu alternative built on ComfyUI and MiniMax H3, with FLUX.3 Klein relighting.

Creator
D3voz
Size
68.3 MB
Stars
614
Updated
CommitOct 30
Age
4d
Commits · 90 Days