Nodes/comfyui-magic-hour/Magic Hour: AI Face Editor
ComfyUI Node

Magic Hour: AI Face Editor

Magic Hour: AI Face Editor

By magichourhq·Created about 16 hours ago·Updated about 15 hours ago· 0
Magic Hour: AI Face Editor
  • image
  • image
  • image_paths
  • credits_charged
◄enhance_facefalse►
◄eye_gaze_horizontal0►
◄eye_gaze_vertical0►
◄eye_open_ratio0►
◄eyebrow_direction0►
◄head_pitch0►
◄head_roll0►
◄head_yaw0►
◄lip_open_ratio0►
◄mouth_grim0►
◄mouth_position_horizontal0►
◄mouth_position_vertical0►
◄mouth_pout0►
◄mouth_purse0►
◄mouth_smile0►

"Turn her head slightly to the left, open the eyes a touch, and give her a small smile" is a sentence no image model reliably obeys. It's the kind of direction where you re-roll forty times and get a different person each time. The AI Face Editor turns those instructions into sliders instead - the pose and expression of a face, dialled directly, without re-generating the photo.

What it does

One image in. Fourteen sliders plus one boolean. It comes back with the same photo, same person, same lighting, but with the head turned, eyes opened, mouth curved. It's a retouch tool, not a generator: nothing else in the frame changes.

That's the useful distinction. Local inpainting can adjust an expression, but only by regenerating the face region and hoping identity survives - which is exactly the tension the KB's identity doc describes, where the reference weight that holds likeness also flattens detail. Here there's nothing to preserve because nothing is being sampled.

The sliders, and the one thing to know about them

The full set, all -100 to 100, all defaulting to 0, all stepping in 5s:

  • Gaze and eyes: eye_gaze_horizontal, eye_gaze_vertical, eye_open_ratio
  • Brows: eyebrow_direction
  • Head pose: head_pitch, head_roll, head_yaw
  • Mouth: lip_open_ratio, mouth_grim, mouth_position_horizontal, mouth_position_vertical, mouth_pout, mouth_purse, mouth_smile

0 means unchanged, not "the neutral value". And the implementation is stricter than the tooltip: sliders sitting at zero aren't sent to the API at all. Which means you can't ask for an explicit zero on a face that's already smiling - the tool can only add expression, not subtract it to neutral. If a source photo is grinning and you want a flat mouth, this node will not get you there; the dial runs from "as it is" to "more so".

Practically: change one or two sliders at a time. head_yaw and eye_gaze_horizontal are the ones you'll actually live in, because they fix the single most common problem with a good photo - the subject isn't looking at camera. mouth_smile in the 20–40 range is a subtle, believable lift; past 60 it reads as a different, more manic person.

enhance_face is a boolean, default off, and it's a detail-recovery pass on the face region. Turn it on when the source face is slightly soft or the image is small. Leave it off when the face already has texture you like - enhancement is generous with skin smoothing, which is the opposite of what you want if you shot this on a real camera.

Inputs and outputs

Only image is genuinely required in the sense that matters, plus whatever sliders you're using. The output trio is the pack standard: image (an IMAGE batch), image_paths (a STRING with the saved file, newline-joined if several), credits_charged (INT).

And the usual pack rule: only the first image of a batch is uploaded. Twenty portraits in one batch gets you one edited portrait.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/magichourhq/comfyui-magic-hour
pip install -r comfyui-magic-hour/requirements.txt

Restart ComfyUI; nodes appear under Magic Hour. Dependencies: magic-hour>=0.87.1 and nothing else - no weights, no GPU work, no model directory to organise. ComfyUI Manager users can search "Magic Hour".

export MAGIC_HOUR_API_KEY=your_key_here

before launching, or a bare key inside comfyui-magic-hour/api_key.txt. It's deliberately not a node field, since widget values are serialised into the workflow.

Where it goes wrong

Extreme values. head_yaw at 100 is a request to rotate a head that was photographed frontally, and the pixels behind the ear were never captured. Everything past about ±60 on the pose sliders starts to look like a different photograph.

Eyes wide open, always. eye_open_ratio up is compelling and quickly uncanny. Slight asymmetry is what real faces do; the slider gives you none unless you also nudge the gaze.

No credits_charged surprise. You do get the number here, which is how you learn that a slider pass costs roughly what an edit costs. Do your slider exploration at low volume, then apply the found settings to the batch.

The web app isn't the API. Magic Hour's consumer site is the same company's engine with a much stingier free tier - small allowances, watermarks, slow queues. The API you're calling from this node is the metered developer surface and behaves differently. Don't judge the node by what a free web account gives you.

My take: this is one of the few nodes in the pack where paying per call genuinely buys something the local stack struggles with. Expression and head pose as continuous dials, without identity drift, is not something you can assemble cheaply from IP-Adapter and an inpaint pass.

CategoryMagic Hour

Inputs (16)

NameTypeDefaultDescription
imageIMAGE—
enhance_faceBOOLEANfalse—
eye_gaze_horizontalFLOAT0-100–100—
eye_gaze_verticalFLOAT0-100–100—
eye_open_ratioFLOAT0-100–100—
eyebrow_directionFLOAT0-100–100—
head_pitchFLOAT0-100–100—
head_rollFLOAT0-100–100—
head_yawFLOAT0-100–100—
lip_open_ratioFLOAT0-100–100—
mouth_grimFLOAT0-100–100—
mouth_position_horizontalFLOAT0-100–100—
mouth_position_verticalFLOAT0-100–100—
mouth_poutFLOAT0-100–100—
mouth_purseFLOAT0-100–100—
mouth_smileFLOAT0-100–100—

Outputs (3)

NameTypeDescription
imageIMAGE—
image_pathsSTRING—
credits_chargedINT—