Track B: Opus 5.5 with a GPT-6.1-Terra reviewer
reviewer
+14:34The current round makes the brows easier to read, especially for Angry and Surprised, but drawing them over the bangs creates a floating, pasted-on look. The new pink mouths also lose some of the depth and clarity of the darker Before mouths.
Woman: Joy and Wink read clearly through the closed eyes; Angry reads through the sharply slanted brows. Neutral and Smile are pleasant but very similar. Sad reads mostly from the brows and small mouth; the eyes still look fairly neutral. Surprised and Shout are harder to tell apart: both mouths are small, horizontal pink shapes, and Surprised lacks a convincing open “O.” Smug reads close to Neutral because the eyes and mouth barely change.
Youth: Joy, Angry and Surprised read through their eye and brow shapes. Wink reads, though the closed eye still shows enough iris to look half-lidded. Smile, Sad and Smug are subtle; Smug especially needs a more asymmetric or self-satisfied mouth. Shout looks more like a small smile than a forceful call.
For both bodies, the lip-sync shapes have regressed: A and O are flattened and similarly pink, I is nearly invisible, U is a tiny mark, and E is a thin smile. White rims and dark corner fragments make several mouths look outlined or detached.
The three highest-impact changes for the next round:
- Integrate the brows with the hair: keep their expressive angles, but break or soften strokes where bangs cross them so they don’t appear painted on top.
- Restore mouth depth: use a dark interior with restrained tongue or lip colour; remove the white rims and corner fragments. Give Shout a substantially taller opening.
- Separate the phonemes: make A tall and open, I wide and narrow, U small and rounded, E wide with visible teeth, and O distinctly round.
agent
+15:07Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && python3 -I - <<'EOF'
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && python3 -I - <<'EOF' p='review/vroid-02/index.html'; s=open(p).read() def rep(a,b): global s assert s.count(a)==1,a; s=s.replace(a,b) rep(""" vec3 col = texture2D(tColor, uv).rgb; int r0 = rid(inf); float p0 = pid(inf); float z0 = z(uv); bool feature = r0 >= 4 && r0 <= 9;""",""" vec3 col = texture2D(tColor, uv).rgb; int r0 = rid(inf); float p0 = pid(inf); float z0 = z(uv); bool feature = r0 >= 4 && r0 <= 9; if (r0 == 9) col = mouthInside(uv, px, col); if (r0 == 8) col = browOverHair(uv, px, col);""") rep(""" vec3 ink(vec3 c) { vec3 h = rgb2hsv(c); return hsv2rgb(vec3(h.x, clamp(h.y, 0.20, 0.45), 0.19)); } void main() {""",""" vec3 ink(vec3 c) { vec3 h = rgb2hsv(c); return hsv2rgb(vec3(h.x, clamp(h.y, 0.20, 0.45), 0.19)); } // How far (px) the mouth's opening runs from this pixel in direction d (up to 64 px). float mouthRun(vec2 uv, vec2 d) { float n = 0.0; for (int s = 1; s <= 64; s++) { vec4 a = texture2D(tInfo, uv + d * float(s)); if (a.a < 0.5 || rid(a) != 9) break; n += 1.0; } return n; } // The open mouth as anime draws it: a dark inside, the upper teeth as a white band under the lip, and the tongue // as a pink mound rising from the lower lip (higher in the middle). A slit too thin for that stays all dark. vec3 mouthInside(vec2 uv, vec2 px, vec3 tex) { float up = mouthRun(uv, vec2(0.0, px.y)), dn = mouthRun(uv, vec2(0.0, -px.y)); float lf = mouthRun(uv, vec2(-px.x, 0.0)), rt = mouthRun(uv, vec2(px.x, 0.0)); float h = up + dn + 1.0, t = (up + 0.5) / h, s = (lf + 0.5) / (lf + rt + 1.0); vec3 deep = vec3(0.30, 0.10, 0.13), tongue = vec3(0.84, 0.44, 0.47); bool white = dot(tex, vec3(1.0 / 3.0)) > 0.88; if (h < 7.0 * uSil) return deep; if (white && t < 0.38) return vec3(0.99, 0.97, 0.96); // upper teeth float top = 0.60 + 0.9 * (s - 0.5) * (s - 0.5); // the tongue's arched top return t > top ? tongue : deep; } // Brows drawn over the bangs: where hair lies just above or below the stroke, the brow is seen through the hair, // so it takes some of the hair's colour and doesn't sit on it like a sticker. vec3 browOverHair(vec2 uv, vec2 px, vec3 col) { float d = 7.0 * uSil; vec4 a = texture2D(tInfo, uv + vec2(0.0, d) * px), b = texture2D(tInfo, uv - vec2(0.0, d) * px); bool ha = a.a > 0.5 && (rid(a) == 3 || rid(a) == 11), hb = b.a > 0.5 && (rid(b) == 3 || rid(b) == 11); if (!(ha || hb)) return col; vec3 hc = ha ? texture2D(tColor, uv + vec2(0.0, d) * px).rgb : texture2D(tColor, uv - vec2(0.0, d) * px).rgb; return mix(col, hc * 0.80, 0.42); } void main() {""") open(p,'w').write(s) EOF grep -n 'uSil' review/vroid-02/index.html | grep -v 'uniform\|px' | headresult, 6 lines
529: if (h < 7.0 * uSil) return deep; 537: float d = 7.0 * uSil; 575: float acc = 0.0, rad = uSil * 3.0; 607: uNear: { value: 0.1 }, uFar: { value: 40 }, uGap: { value: 0.012 }, uSil: { value: 2 }, uInner: { value: 1 } }); 1406: u.uSil.value = THREE.MathUtils.clamp(1.75 * ppm * 0.0042, dpr, 2.6 * dpr); 1407: u.uInner.value = Math.max(dpr * 0.75, u.uSil.value * 0.55);Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && python3 -I - <<'EOF'
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && python3 -I - <<'EOF' p='review/vroid-02/index.html'; s=open(p).read() def rep(a,b): global s assert s.count(a)==1,a; s=s.replace(a,b) rep(""" // How far (px) the mouth's opening runs from this pixel in direction d (up to 64 px). float mouthRun(vec2 uv, vec2 d) { float n = 0.0; for (int s = 1; s <= 64; s++) { vec4 a = texture2D(tInfo, uv + d * float(s)); if (a.a < 0.5 || rid(a) != 9) break; n += 1.0; }""", """ // How far (px) the mouth's opening runs from this pixel in direction d (2 px steps, up to 128 px). float mouthRun(vec2 uv, vec2 d) { float n = 0.0; for (int s = 1; s <= 64; s++) { vec4 a = texture2D(tInfo, uv + d * float(2 * s)); if (a.a < 0.5 || rid(a) != 9) break; n += 2.0; }""") rep("if (h < 7.0 * uSil) return deep;","if (h < 3.0 * uSil) return deep;") rep(""" float d = 7.0 * uSil;""",""" float d = 3.5 * uSil;""") rep(""" if (!(ha || hb)) return col; vec3 hc = ha ? texture2D(tColor, uv + vec2(0.0, d) * px).rgb : texture2D(tColor, uv - vec2(0.0, d) * px).rgb; return mix(col, hc * 0.80, 0.42);""",""" if (!(ha || hb)) return col; vec3 hc = ha ? texture2D(tColor, uv + vec2(0.0, d) * px).rgb : texture2D(tColor, uv - vec2(0.0, d) * px).rgb; return mix(col, hc * 0.80, ha && hb ? 0.42 : 0.2);""") open(p,'w').write(s) EOF cat > $WORK/expr_list.txt <<'EOF' # expression recipes: name|VRoid shapes baked at build (Fcl_* names, weight; ';' between; 'none' = neutral) # the 14 the sheets show; keep these names (front_sheet.py reads them). Edit the recipes freely. neutral|none joy|Fcl_BRW_Joy:1;Fcl_EYE_Joy:1;Fcl_MTH_Joy:0.85 smile|Fcl_BRW_Fun:1;Fcl_EYE_Fun:0.75;Fcl_MTH_Fun:1 angry|Fcl_BRW_Angry:1.4;Fcl_EYE_Angry:1;Fcl_MTH_Angry:1 sad|Fcl_BRW_Sorrow:1.3;Fcl_EYE_Sorrow:1.3;Fcl_MTH_Sorrow:1 surprised|Fcl_BRW_Surprised:1.2;Fcl_EYE_Surprised:1;Fcl_MTH_O:0.8 shout|Fcl_BRW_Angry:1.3;Fcl_EYE_Angry:0.8;Fcl_MTH_A:1.15 wink|Fcl_EYE_Close_R:1.15;Fcl_BRW_Fun:0.6;Fcl_MTH_Fun:1 smug|Fcl_EYE_Close:0.45;Fcl_BRW_Angry:0.4;Fcl_MTH_Fun:0.8;Fcl_MTH_SkinFung_R:1 lip_A|Fcl_MTH_A:1 lip_I|Fcl_MTH_I:1 lip_U|Fcl_MTH_U:1 lip_E|Fcl_MTH_E:1 lip_O|Fcl_MTH_O:1 EOF time $KIT/build_expr.sh $WORK/expr_list.txt joy smile sad surprised shout wink smugresult, 11 lines
built joy built smile built sad built surprised built shout built wink built smug real 1m41.530s user 1m28.015s sys 1m10.896s
agent
+17:09Writing round-1 notes while I build the rest.
Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cat >> $WORK/NOTES.md <<'EOF'
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cat >> $WORK/NOTES.md <<'EOF' ## Round 1 (12:50-13:08, ~18 min incl. a slow 8 min shoot) Changed: - Renderer: brows drawn over the bangs (vertex shader lifts the brow role 4 cm toward the camera, `BROW_LIFT`). - Renderer: the open mouth no longer a flat dark fill; a new material kind 10 colours VRoid's mouth texture as maroon inside, pink tongue, white teeth. - Recipes: split the ALL_* shapes into BRW/EYE/MTH parts so brows could be pushed past 1 (angry 1.4, sad 1.3, surprised 1.2) while open mouths were scaled down (joy 0.6, surprised 0.6, lips A/O 0.75); wink and smug got brows. Sheets: brows now carry angry, sad and surprised on both bodies (before, the bangs hid them). Mouths went too pink and small: the tongue fills the whole opening, so open mouths read as pink lozenges with white rims. Terra r1 critique (summary): brows read but look pasted on top of the bangs; pink mouths lost depth, white rims and dark corner fragments; Surprised vs Shout too alike; Smug ~ Neutral; Smile ~ Neutral; lip shapes regressed (A/O flat, I invisible, U tiny, E thin). Top 3: integrate brows with hair, restore dark mouth depth, separate the phonemes. Response: agreed with all three. Brows: keep them over the bangs (decision below) but tint them toward the hair where hair lies above and below the stroke (see-through brows). Mouth: a composite-pass inside: dark, upper teeth band, tongue only as an arched mound from the lower lip, thin slits all dark. Lips back to weight 1; surprised uses MTH_O for a round mouth; shout opens to 1.15. Smug: heavier lids and a one-sided fang (MTH_SkinFung_R) for asymmetry. EOF cd $LOWMARCH_REPO && time $KIT/build_expr.sh $WORK/expr_list.txt lip_A lip_E lip_O && time $KIT/shoot_expr.sh $LOWMARCH_REPO/review/vroid-02/index.html r2 && $KIT/sheets.sh r2 "Round 2: dark mouth inside, see-through brows, rounder phonemes"
result, 14 lines
built lip_A built lip_E built lip_O real 0m54.537s user 0m39.264s sys 0m29.899s shot r2 real 6m46.198s user 0m32.412s sys 0m27.529s ~/Projects/lowmarch-bench/work/b/sheet_r2_woman.png (1968, 1144) ~/Projects/lowmarch-bench/work/b/sheet_r2_youth.png (1968, 1144)
Read~/Projects/lowmarch-bench/work/b/sheet_r2_woman.png
input
{ "file_path": "~/Projects/lowmarch-bench/work/b/sheet_r2_woman.png" }result
no output
Read~/Projects/lowmarch-bench/work/b/sheet_r2_youth.png
input
{ "file_path": "~/Projects/lowmarch-bench/work/b/sheet_r2_youth.png" }result
no output
Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && $KIT/terra_review.sh r2 $WORK/sheet_r2_woman.png $WORK/sheet_r2_youth.png; date
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && $KIT/terra_review.sh r2 $WORK/sheet_r2_woman.png $WORK/sheet_r2_youth.png; date
result, 13 lines
exit=0 The darker mouth interiors and pink lower shading give open mouths more depth than Before. The eyebrows are easier to read, but their visibility through solid hair sometimes looks like an overlay. **Woman:** Neutral and Smile read clearly, though Smile is subtle. Joy reads as happy through the closed eyes, but its small mouth is less exuberant than Before. Angry reads well from the inward-sloping brows. Sad reads mainly from the brows; the tiny mouth could also suggest uncertainty. Surprised looks more like speaking: the mouth is a narrow horizontal opening. Shout reads as talking because its mouth is only moderately open. Wink is clear, though the closed eye has a sharp, triangular lash shape. Smug is too close to Neutral. A and O are readable but similar; I and E are very slight, while U is a tiny dot. **Youth:** Neutral, Smile and Angry read clearly. Joy reads as pleased, though the half-closed eyes and smaller mouth soften it compared with Before. Sad looks tired or neutral because the mouth is nearly flat. Surprised has wide eyes, but its small mouth weakens the reaction. Shout resembles A. Wink reads, although the closed eye looks partly open; Smug mostly reads as sleepy. A, I and O are distinct, but U is pin-sized and E is close to a smile. The three most useful changes for the next round: 1. Make Surprised mouths taller and rounder, and give Shout a distinctly larger opening; retain the new dark interiors. 2. Strengthen Sad with a visible downturned mouth and inner brows raised; make Smug asymmetric in both eyelids and mouth. 3. Refine brow visibility through bangs: soften or interrupt strokes at hair boundaries, especially the long, dark Angry and Sad brows. Enlarge U slightly and widen E for clearer lip sync. Fri Oct 30 01:08:37 PM PST 2026
reviewer
+25:29The darker mouth interiors and pink lower shading give open mouths more depth than Before. The eyebrows are easier to read, but their visibility through solid hair sometimes looks like an overlay.
Woman: Neutral and Smile read clearly, though Smile is subtle. Joy reads as happy through the closed eyes, but its small mouth is less exuberant than Before. Angry reads well from the inward-sloping brows. Sad reads mainly from the brows; the tiny mouth could also suggest uncertainty. Surprised looks more like speaking: the mouth is a narrow horizontal opening. Shout reads as talking because its mouth is only moderately open. Wink is clear, though the closed eye has a sharp, triangular lash shape. Smug is too close to Neutral. A and O are readable but similar; I and E are very slight, while U is a tiny dot.
Youth: Neutral, Smile and Angry read clearly. Joy reads as pleased, though the half-closed eyes and smaller mouth soften it compared with Before. Sad looks tired or neutral because the mouth is nearly flat. Surprised has wide eyes, but its small mouth weakens the reaction. Shout resembles A. Wink reads, although the closed eye looks partly open; Smug mostly reads as sleepy. A, I and O are distinct, but U is pin-sized and E is close to a smile.
The three most useful changes for the next round:
- Make Surprised mouths taller and rounder, and give Shout a distinctly larger opening; retain the new dark interiors.
- Strengthen Sad with a visible downturned mouth and inner brows raised; make Smug asymmetric in both eyelids and mouth.
- Refine brow visibility through bangs: soften or interrupt strokes at hair boundaries, especially the long, dark Angry and Sad brows. Enlarge U slightly and widen E for clearer lip sync.
agent
+25:48Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && sed -n 130,170p tools/vrm.py; grep -rn 'Close_R\|_R"\|_L"' tools/vroid_portrait.py | head -5
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && sed -n 130,170p tools/vrm.py; grep -rn 'Close_R\|_R"\|_L"' tools/vroid_portrait.py | head -5
result, 40 lines
out.append(np.stack([posed[j] @ ibm[k] for k, j in enumerate(sk["joints"])])) return out def primitives(self, morphs=None, pose=None): """List of dicts: mesh, material (name), mi, pos (N,3), nrm (N,3), uv (N,2), tri (M,3). morphs: {target name substring: weight} applied to meshes that have targets. pose: {bone name: 3x3 rotation} -> linear blend skinning.""" out = [] mesh_skin = {n["mesh"]: n.get("skin") for n in self.j["nodes"] if "mesh" in n} jm = self.skin_matrices(pose) if pose else None for mi_, mesh in enumerate(self.j["meshes"]): names = mesh.get("extras", {}).get("targetNames") or \ mesh["primitives"][0].get("extras", {}).get("targetNames") or [] for pr in mesh["primitives"]: at = pr["attributes"] pos = self.accessor(at["POSITION"]).astype(np.float64).copy() nrm = self.accessor(at["NORMAL"]).astype(np.float64) if "NORMAL" in at else np.zeros_like(pos) uv = self.accessor(at["TEXCOORD_0"]).astype(np.float64) if "TEXCOORD_0" in at else np.zeros((len(pos), 2)) if morphs and pr.get("targets"): for key, wgt in morphs.items(): for ti, tn in enumerate(names): if tn.endswith(key) and "POSITION" in pr["targets"][ti]: pos += wgt * self.accessor(pr["targets"][ti]["POSITION"]) J = Wt = None sk = mesh_skin.get(mi_) if sk is not None and "JOINTS_0" in at: J = self.accessor(at["JOINTS_0"]).astype(np.int64) Wt = self.accessor(at["WEIGHTS_0"]).astype(np.float64) if jm is not None: M = (jm[sk][J] * Wt[:, :, None, None]).sum(1) # (N,4,4) pos = np.einsum("nij,nj->ni", M[:, :3, :3], pos) + M[:, :3, 3] nrm = np.einsum("nij,nj->ni", M[:, :3, :3], nrm) tri = self.accessor(pr["indices"]).reshape(-1, 3).astype(np.int64) mi = pr.get("material", 0) d = dict(mesh=mesh.get("name"), material=self.j["materials"][mi]["name"], mi=mi, pos=pos, nrm=nrm, uv=uv, tri=tri) if J is not None: d.update(J=J, W=Wt, jnames=[self.j["nodes"][n].get("name", "") for n in self.j["skins"][sk]["joints"]]) out.append(d) return outBashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && grep -n 'def \|VRM(' tools/vroid_portrait.py | sed -n 1,60p | grep -i 'vrm\|load\|path' ; grep -rn '\.vrm"' tools/vroid_portrait.py art/cast/*.py 2>/dev/null | head -5
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && grep -n 'def \|VRM(' tools/vroid_portrait.py | sed -n 1,60p | grep -i 'vrm\|load\|path' ; grep -rn '\.vrm"' tools/vroid_portrait.py art/cast/*.py 2>/dev/null | head -5result, 8 lines
731: def __init__(self, path, morphs=None, pose=BUST_POSE, shape=None, hair_from=None, costume=None, 737: self.v = VRM(path) 750: hv = VRM(hair_from) art/cast/cast.py:81:MH_HEAD = "base_male/Base_Male.vrm" art/cast/cast.py:89:MH_HEAD_F = "base_female/Base_Female.vrm" art/cast/cast.py:110:MASS_MUSCULAR = dict(base="lowmarch_bodies/base_masc.vrm", muscle=1.0, weight=0.75, gain=2.6, torso_gain=0.5, targets={ art/cast/cast.py:114:MASS_HEAVY = dict(base="lowmarch_bodies/base_masc.vrm", muscle=0.6, weight=1.0, gain=2.6, torso_gain=0.7, targets={ art/cast/cast.py:223:_MASC, _FEM = "lowmarch_bodies/base_masc.vrm", "lowmarch_bodies/base_fem.vrm"Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && f=$(find / -name base_fem.vrm -path '*lowmarch_bodies*' 2>/dev/null | head -1); echo $f; python3 -I -c "
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && f=$(find / -name base_fem.vrm -path '*lowmarch_bodies*' 2>/dev/null | head -1); echo $f; python3 -I -c " import sys; sys.path.insert(0,'tools') from vrm import VRM import numpy as np v=VRM('$f') for mesh in v.j['meshes']: names=mesh.get('extras',{}).get('targetNames') or mesh['primitives'][0].get('extras',{}).get('targetNames') or [] for pr in mesh['primitives']: if not pr.get('targets'): continue for ti,tn in enumerate(names): if tn.endswith('EYE_Close_R') and 'POSITION' in pr['targets'][ti]: d=v.accessor(pr['targets'][ti]['POSITION']); p=v.accessor(pr['attributes']['POSITION']) m=np.linalg.norm(d,axis=1)>1e-5 if m.any(): print(tn, m.sum(), p[m,0].mean()) "result, 2 lines
Command did not complete within its 120s timeout and was moved to the background (ID: b6famq2m3). Output is being written to: /tmp/claude-1000/-home-severian-Projects/d2fdcea1-0c48-4814-81a2-89cf837a9b4f/tasks/b6famq2m3.output. You will be notified when it completes. It is running now: do not start a second copy while it runs. If it is no longer needed, stop it with TaskStop. No output had been read from it when it was moved. A program that buffers its output, or a pipe into one such as tail, can keep the output file empty until the command finishes. To check interim output, use Read on that file path. Session cwd remains ~/Projects/lowmarch-bench/c; directory changes made by the backgrounded command do not apply to subsequent commands.
ToolSearchselect:TaskStop
input
{ "query": "select:TaskStop", "max_results": 1 }result
no output
TaskStop{"task_id":"b6famq2m3"}
input
{ "task_id": "b6famq2m3" }result, 1 line
{"message":"Successfully stopped task: b6famq2m3 (source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && f=$(find / -name base_fem.vrm -path '*lowmarch_bodies*' 2>/dev/null | head -1); echo $f; python3 -I -c \"\nimport sys; sys.path.insert(0,'tools')\nfrom vrm import VRM\nimport numpy as np\nv=VRM('$f')\nfor mesh in v.j['meshes']:\n names=mesh.get('extras',{}).get('targetNames') or mesh['primitives'][0].get('extras',{}).get('targetNames') or []\n for pr in mesh['primitives']:\n if not pr.get('targets'): continue\n for ti,tn in enumerate(names):\n if tn.endswith('EYE_Close_R') and 'POSITION' in pr['targets'][ti]:\n d=v.accessor(pr['targets'][ti]['POSITION']); p=v.accessor(pr['attributes']['POSITION'])\n m=np.linalg.norm(d,axis=1)>1e-5\n if m.any(): print(tn, m.sum(), p[m,0].mean())\n\")","task_id":"b6famq2m3","task_type":"local_bash","command":"source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && f=$(find / -name base_fem.vrm -path '*lowmarch_bodies*' 2>/dev/null | head -1); echo $f; python3 -I -c \"\nimport sys; sys.path.insert(0,'tools')\nfrom vrm import VRM\nimport numpy as np\nv=VRM('$f')\nfor mesh in v.j['meshes']:\n names=mesh.get('extras',{}).get('targetNames') or mesh['primitives'][0].get('extras',{}).get('targetNames') or []\n for pr in mesh['primitives']:\n if not pr.get('targets'): continue\n for ti,tn in enumerate(names):\n if tn.endswith('EYE_Close_R') and 'POSITION' in pr['targets'][ti]:\n d=v.accessor(pr['targets'][ti]['POSITION']); p=v.accessor(pr['attributes']['POSITION'])\n m=np.linalg.norm(d,axis=1)>1e-5\n if m.any(): print(tn, m.sum(), p[m,0].mean())\n\""}Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && python3 -I - <<'EOF'
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && python3 -I - <<'EOF' p='tools/vrm.py'; s=open(p).read() a=""" if morphs and pr.get("targets"): for key, wgt in morphs.items(): for ti, tn in enumerate(names): if tn.endswith(key) and "POSITION" in pr["targets"][ti]: pos += wgt * self.accessor(pr["targets"][ti]["POSITION"])""" b=""" if morphs and pr.get("targets"): base = pos.copy() for key, wgt in morphs.items(): key, _, side = key.partition("@") # "Fcl_MTH_Fun@R": that side of the face only w = wgt if not side else wgt * self._side_mask(pr, names, base, side) for ti, tn in enumerate(names): if tn.endswith(key) and "POSITION" in pr["targets"][ti]: d = self.accessor(pr["targets"][ti]["POSITION"]) pos += (w[:, None] if side else w) * d""" assert s.count(a)==1; s=s.replace(a,b) a=""" def primitives(self, morphs=None, pose=None):""" b=""" def _side_mask(self, pr, names, base, side): \"\"\"Per-vertex weight 0..1 for one side of the face ('L' or 'R', as VRoid's Fcl_EYE_Close_L/R name them): full on that side, fading over 3 cm across the midline so a one-sided smirk bends rather than tears.\"\"\" sign = 1.0 for ti, tn in enumerate(names): if tn.endswith("EYE_Close_" + side) and "POSITION" in pr["targets"][ti]: d = self.accessor(pr["targets"][ti]["POSITION"]) m = np.linalg.norm(d, axis=1) > 1e-6 if m.any(): sign = 1.0 if base[m, 0].mean() > 0 else -1.0 break return np.clip(0.5 + sign * base[:, 0] / 0.03, 0.0, 1.0) def primitives(self, morphs=None, pose=None):""" assert s.count(a)==1; s=s.replace(a,b) open(p,'w').write(s) EOF grep -n 'rest\b\|"rest"\|rest=' art/cast/*.py review/vroid-02/build.py 2>/dev/null | head; grep -n 'morphs' tools/vroid_portrait.py | headresult, 12 lines
art/cast/cast.py:232:# archetype. What a face does at rest (a frown, half-closed eyes) and age lines are drawn when the figure is built art/cast/cast.py:273:PORTRAIT = { # at rest and age lines: name -> shape extras (tools/vroid_portrait.py) art/cast/cast.py:274: "arm_m_strong": dict(rest={"Fcl_BRW_Angry": 0.4}), art/cast/cast.py:275: "arm_m_heroic": dict(rest={"Fcl_BRW_Angry": 0.25}), art/cast/cast.py:276: "arm_m_brute": dict(rest={"Fcl_EYE_Close": 0.28, "Fcl_BRW_Angry": 0.75}), art/cast/cast.py:277: "arm_m_old": dict(age_lines=0.8, blush=0.0, rest={"Fcl_EYE_Angry": 0.4, "Fcl_EYE_Close": 0.25, "Fcl_BRW_Angry": 0.5, art/cast/cast.py:279: "arm_f_old": dict(age_lines=0.6, blush=0.3, rest={"Fcl_EYE_Close": 0.22, "Fcl_MTH_Down": 0.2}), art/cast/cast.py:280: "arm_f_strong": dict(rest={"Fcl_BRW_Angry": 0.2}), art/cast/cast.py:321: shape=dict(head=0.95, leg_len=1.04, shoulder_w=0.008, rest={"Fcl_BRW_Angry": 0.35}), art/cast/cast.py:332: shape=dict(head=0.95, leg_len=1.04, shoulder_w=0.008, rest={"Fcl_BRW_Angry": 0.35}), 731: def __init__(self, path, morphs=None, pose=BUST_POSE, shape=None, hair_from=None, costume=None, 740: for k_, w_ in (morphs or {}).items():Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && sed -n 736,770p tools/vroid_portrait.py
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && sed -n 736,770p tools/vroid_portrait.py
result, 35 lines
self.mouth = mouth self.v = VRM(path) sh = dict(NEUTRAL, **(shape or {})) mo = dict(sh["rest"]) for k_, w_ in (morphs or {}).items(): mo[k_] = min(1.0, mo.get(k_, 0.0) + w_) # everything is built in the reference pose (arms down), then posed through the skeleton target_pose = bone_pose(self.v, sh, pose) pose = bone_pose(self.v, sh, ref_pose) self.pose_now = target_pose self.pose_ref = pose self.shape = sh self.prims = self.v.primitives(mo, pose) if hair_from: # swap in another model's hair hv = VRM(hair_from) hp = hv.primitives(None, ref_pose) hroles = [role_of(p["material"]) for p in hp] src = landmarks(hp, hroles) dst = landmarks(self.prims, [role_of(p["material"]) for p in self.prims]) k = (abs(dst["eyeR"][0] - dst["eyeL"][0]) / abs(src["eyeR"][0] - src["eyeL"][0]) + (dst["eye_y"] - dst["chin"][1]) / (src["eye_y"] - src["chin"][1])) / 2 c_src = (src["eyeL"] + src["eyeR"]) / 2 c_dst = (dst["eyeL"] + dst["eyeR"]) / 2 hair = [p for p in hp if "_HAIR" in p["material"]] for p in hair: p["pos"] = c_dst + (p["pos"] - c_src) * k n_ = len(p["pos"]) # rides our head bone from here on p["J"], p["W"], p["jnames"] = np.zeros((n_, 4), np.int64), np.tile([1.0, 0, 0, 0], (n_, 1)), ["J_Bip_C_Head"] self.prims = [p for p in self.prims if "_HAIR" not in p["material"]] + hair self._hair_v = hv if hair_trim: # drop hair pieces reaching past |x| = hair_trim (e.g. twin tails) self.prims = [p for p in self.prims if "_HAIR" not in p["material"] or np.abs(p["pos"][np.unique(p["tri"]), 0]).max() <= hair_trim] self.roles = [p.get("role") or role_of(p["material"]) for p in self.prims] self.lm = landmarks(self.prims, self.roles)Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && cat > $WORK/expr_list.txt <<'EOF'
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cd $LOWMARCH_REPO && cat > $WORK/expr_list.txt <<'EOF' # expression recipes: name|VRoid shapes baked at build (Fcl_* names, weight; ';' between; 'none' = neutral) # the 14 the sheets show; keep these names (front_sheet.py reads them). Edit the recipes freely. # a key ending @L or @R applies to that side of the face only (tools/vrm.py), for one-sided looks neutral|none joy|Fcl_BRW_Joy:1;Fcl_EYE_Joy:1;Fcl_MTH_Joy:1 smile|Fcl_BRW_Fun:1;Fcl_EYE_Fun:0.75;Fcl_MTH_Fun:1 angry|Fcl_BRW_Angry:1.4;Fcl_EYE_Angry:1;Fcl_MTH_Angry:1 sad|Fcl_BRW_Sorrow:1.4;Fcl_EYE_Sorrow:1.3;Fcl_MTH_Sorrow:0.6;Fcl_MTH_Down:1 surprised|Fcl_BRW_Surprised:1.2;Fcl_EYE_Surprised:1;Fcl_MTH_O:1.15;Fcl_MTH_A:0.25 shout|Fcl_BRW_Angry:1.3;Fcl_EYE_Angry:0.8;Fcl_MTH_A:1.35;Fcl_MTH_O:0.3 wink|Fcl_EYE_Joy_R:1;Fcl_EYE_Close_R:0.3;Fcl_BRW_Fun:0.6;Fcl_MTH_Fun:1 smug|Fcl_EYE_Close@L:0.55;Fcl_EYE_Close@R:0.3;Fcl_BRW_Angry@L:0.5;Fcl_MTH_Fun@R:1;Fcl_MTH_Up@R:0.5 lip_A|Fcl_MTH_A:1 lip_I|Fcl_MTH_I:1 lip_U|Fcl_MTH_U:1.35 lip_E|Fcl_MTH_E:1;Fcl_MTH_I:0.35 lip_O|Fcl_MTH_O:1 EOF sed -i 's/return mix(col, hc \* 0.80, ha \&\& hb ? 0.42 : 0.2);/return mix(col, hc * 0.80, ha \&\& hb ? 0.55 : 0.28);/' review/vroid-02/index.html && grep -n 'ha && hb ?' review/vroid-02/index.html && time $KIT/build_expr.sh $WORK/expr_list.txt joy sad surprised shout wink smug
result, 11 lines
542: return mix(col, hc * 0.80, ha && hb ? 0.55 : 0.28); built joy built sad built surprised built shout built wink built smug real 2m12.676s user 1m28.916s sys 1m10.189s
Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && cat >> $WORK/NOTES.md <<'EOF'
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && cat >> $WORK/NOTES.md <<'EOF' (Correction: round 1 actually ran 12:50-12:59; my earlier clock estimate was wrong.) ## Round 2 (12:59-13:08, ~9 min) Changed: - Composite pass `mouthInside`: for mouth pixels it measures the opening (runs up/down/left/right in the info buffer) and draws a dark maroon inside, a white upper-teeth band only near the top, and the tongue as an arched pink mound from the lower lip. Openings thinner than ~3 outline widths stay all dark (I, E, slits). - Composite pass `browOverHair`: a brow pixel with hair just above and below it takes 42% of the hair's colour (20% at the edge of a lock), so brows read through the bangs instead of sitting on them. - Recipes: lips back to 1; surprised uses MTH_O; shout MTH_A 1.15; joy mouth 0.85; sad eyes 1.3; smug heavier lids. Sheets: open mouths now read as anime mouths (dark inside, tongue low), clearly better than Before's flat brown and r1's pink. Smug still ~ neutral (the fang shape shows nothing on these faces). Smile subtle. Terra r2 critique (summary): darker interiors give depth; brows readable but sometimes overlay-like. Surprised looks like speaking (narrow opening); Shout ~ A; Sad mouth flat; Smug ~ Neutral / sleepy; Wink lash shape sharp (woman) and half-open (youth); U pin-sized; E close to a smile. Top 3: taller/rounder Surprised and larger Shout; Sad mouth downturned and Smug asymmetric in lids and mouth; soften brows at hair boundaries, bigger U, wider E. Response: agreed with all. Added one-sided shape keys (`Fcl_X@L` / `@R` in a recipe, `VRM._side_mask` in tools/vrm.py, side taken from VRoid's own EYE_Close_L/R, 3 cm fade across the midline) so smug gets one heavier lid, one lowered brow and a one-sided smile. Surprised MTH_O 1.15 + A 0.25; shout A 1.35 + O 0.3; sad MTH_Down; wink uses the ^ eye (EYE_Joy_R); U 1.35; E + 0.35 I; brow-over-hair tint 55% / 28%. Not acted on: "interrupt brow strokes at hair boundaries" beyond the tint, since breaking them makes angry/sad brows lose their slope, which is the point of drawing them over the hair. EOF cd $LOWMARCH_REPO && $KIT/build_expr.sh $WORK/expr_list.txt lip_U lip_E && time $KIT/shoot_expr.sh $LOWMARCH_REPO/review/vroid-02/index.html r3 && $KIT/sheets.sh r3 "Round 3: one-sided smug, rounder surprise, bigger shout, sad mouth"; date
result, 10 lines
built lip_U built lip_E shot r3 real 8m21.942s user 0m35.644s sys 0m29.030s ~/Projects/lowmarch-bench/work/b/sheet_r3_woman.png (1968, 1144) ~/Projects/lowmarch-bench/work/b/sheet_r3_youth.png (1968, 1144) Fri Oct 30 01:23:00 PM PST 2026
Read~/Projects/lowmarch-bench/work/b/sheet_r3_woman.png
input
{ "file_path": "~/Projects/lowmarch-bench/work/b/sheet_r3_woman.png" }result
no output
Read~/Projects/lowmarch-bench/work/b/sheet_r3_youth.png
input
{ "file_path": "~/Projects/lowmarch-bench/work/b/sheet_r3_youth.png" }result
no output
Bashsource ~/Projects/lowmarch-bench/kit/env-b.sh && $KIT/terra_review.sh r3 $WORK/sheet_r3_woman.png $WORK/sheet_r3_youth.png; date
command
source ~/Projects/lowmarch-bench/kit/env-b.sh && $KIT/terra_review.sh r3 $WORK/sheet_r3_woman.png $WORK/sheet_r3_youth.png; date
result, 15 lines
exit=0 The current round improves the brows and adds useful mouth depth, but several expressions still depend too heavily on the mouth or have features that look drawn over the hair. **Woman:** Joy and Smile read clearly; the closed eyes and smile shape distinguish them well. Angry reads through the sharply lowered brows. Wink is recognizable, though the closed eye looks like a cluster of thin lines. Smug reads as one-sided, but the sloping mouth suggests uncertainty more than confidence. Sad has convincing brows, yet the mouth sits unusually low, near the chin. Surprised looks more like speaking: the eyes are close to neutral and the mouth is wide rather than round. Shout has a larger mouth, but still resembles an open smile. **Youth:** Joy, Smile and Angry read clearly. Surprised is stronger than the woman’s, with widened eyes and raised brows, though it resembles Shout. Wink reads as a half-lidded eye rather than a fully closed wink. Smug has useful asymmetry, but the tilted mouth looks subdued. Sad is weak: the mouth is almost a straight line, and its low placement makes it feel detached. Across both sheets, the dark brows sometimes appear to sit on top of the bangs. Several open mouths have angular edges and a conspicuous dark-to-pink split. The **U mouth nearly disappears** on the youth and becomes a tiny vertical mark on the woman. The revised O mouths also look narrower and less rounded than Before. The three highest-impact changes for the next round: 1. Rebuild **U and O** as visible, rounded openings: make U a small pursed oval and O a larger, taller oval, with smooth contours. 2. Raise the **Sad mouths** to the normal mouth position and give them a subtle downturned curve; soften the low-contrast youth mouth enough to remain visible. 3. Separate **Surprised and Shout**: widen and round the surprised eyes and mouth; give Shout a taller, larger opening and stronger brows. Keep brow segments behind the bangs. Fri Oct 30 01:23:25 PM PST 2026



