Takazudo Modular Docs

Type to search...

to open search from anywhere

Product Photo Maker Sub-Package

Product Photo Maker Sub-Package

CLI tool for generating standardized 1600x1600 product photos with ML-based background removal, fabric texture background compositing, and programmatic shadow generation.

Purpose

Produces consistent, marketplace-ready product photos from raw source images. Used before the main image pipeline (image-processor) to prepare product photos for the website and Mercari listings.

Source images from manufacturers typically come with white backgrounds. This tool removes the background, composites the product onto the shop’s fabric texture, and adds realistic shadows — all locally with no API costs.

Before / After

Source image from manufacturer (white background):

Original white background product photo

After processing with --shadow --float (background removed, composited on fabric texture, floating shadow added):

Processed product photo with shadow on fabric texture

Sub-Package Structure

sub-packages/product-photo-maker/
├── package.json
├── CLAUDE.md
├── assets/
│   └── default-bg.jpg          # Bundled fabric texture background (1.7MB)
├── bin/
│   └── make-product-photo.mjs  # CLI entry point
└── src/
    ├── make-product-photo.mjs  # Core processing logic
    └── shadow.mjs              # Programmatic shadow generation

Processing Pipeline

graph TB
    A[Source Image] --> B{--no-bg-removal?}
    B -->|Yes| D[Read file as-is]
    B -->|No| C[ML Background Removal<br/>@imgly/background-removal-node]
    C --> E{--no-trim?}
    E -->|Yes| F[Keep as-is]
    E -->|No| G[Trim transparent pixels]
    D --> H[Resize to 1440x1440 container<br/>fit: contain]
    F --> H
    G --> H
    H --> I{--shadow?}
    I -->|No| J[Composite onto background]
    I -->|Yes| K[Generate shadow layers]
    K --> L[Composite: bg → shadows → product]
    J --> M[Output JPEG<br/>quality: 90]
    L --> M

Output Specifications

PropertyValue
Canvas size1600x1600 px
Container size1440x1440 px (90% of canvas)
Container offset80px from each edge
Output formatJPEG, quality 90
Default backgroundFabric texture (assets/default-bg.jpg)
Product fittingobject-fit: contain behavior

Shadow System

The shadow system generates multiple layers that composite together for a realistic result. All shadow generation is 100% programmatic using sharp — no API calls, no ML models, zero cost.

Why Shadows Matter

Without shadows, products composited onto the texture look like they’re “floating” — a flat cutout pasted on a background:

Product without shadow - looks flat

With shadows, the product feels like it’s physically present on the surface:

Product with floating shadow - realistic

Shadow Modes

Two shadow modes for different product types:

Floating (--shadow --float)

For Eurorack modular synth panels. The panel appears to hover above the surface with a projected shadow beneath it.

Shadow layers (5 total):

  1. Vignette — asymmetric darkening from upper-left light source
  2. Projected shadow — product alpha squashed to 25% height, placed below product
  3. Bottom-only wide shadow — blur 120, gradient-masked, large offset
  4. Bottom-only medium shadow — blur 52, gradient-masked, medium offset
  5. Contact shadow — tight, sharp shadow at product edge
Floating shadow example - Eurorack module Floating shadow example - M3S module

Grounded (--shadow)

For desktop products (MIDI controllers, standalone units). The product sits on the surface with a tight contact shadow.

Shadow layers (4 total, no projected shadow):

  1. Vignette — same asymmetric darkening
  2. Bottom-only wide shadow — blur 100, small offset
  3. Bottom-only medium shadow — blur 40, small offset
  4. Contact shadow — tight shadow directly beneath product
Grounded shadow example - N32B desktop controller

Shadow Implementation Details

All shadow layers are generated from the product’s alpha channel:

// Extract alpha as raw single-channel buffer
const alphaRaw = await sharp(productPngBuffer)
  .extractChannel(3)
  .raw()
  .toBuffer();

// Each shadow layer: offset alpha → blur → scale opacity → black RGBA
const blurredAlpha = await sharp(offsetAlpha, {
  raw: { width: 1600, height: 1600, channels: 1 },
})
  .blur(blurRadius)
  .linear(opacity, 0)
  .png()
  .toBuffer();

Key techniques:

  • Alpha extraction: extractChannel(3) gets the product silhouette
  • Projected shadow: Alpha cropped to bounding box, squashed vertically with resize(bw, newH, { fit: 'fill' }), placed below product
  • Gradient masking: Bottom-only shadows use a per-pixel gradient mask that fades from 0 (top of product) to 1 (below product)
  • Vignette: Per-pixel distance calculation from light source position, using (distSq / spreadSq) ** 0.75 for natural falloff
  • Black RGBA layers: Each shadow is a black canvas with the blurred alpha as transparency, composited with over blend mode

CLI Usage

node ./sub-packages/product-photo-maker/bin/make-product-photo.mjs <input> [...more] [options]

Options

OptionShortDescription
--output <dir>-oOutput directory (default: __inbox/prod-imgs-exported/)
--with-bg <path>Custom background image instead of default fabric texture
--no-bg-removalSkip ML background removal (use image as-is)
--no-trimSkip trimming after background removal
--shadowAdd directional shadow (grounded, upper-left light)
--floatUse floating shadow for modular synth panels (implies --shadow)
--help-hShow help message

Examples

# Basic: bg removal + trim + fabric texture (no shadow)
node ./sub-packages/product-photo-maker/bin/make-product-photo.mjs ./imgs/product.jpg

# Eurorack module with floating shadow
node ./sub-packages/product-photo-maker/bin/make-product-photo.mjs panel.jpg --shadow --float

# Desktop product with grounded shadow
node ./sub-packages/product-photo-maker/bin/make-product-photo.mjs controller.jpg --shadow

# Edge-to-edge product photo (skip bg removal) with grounded shadow
node ./sub-packages/product-photo-maker/bin/make-product-photo.mjs photo.jpg --no-bg-removal --shadow

# Multiple images
node ./sub-packages/product-photo-maker/bin/make-product-photo.mjs img1.jpg img2.png --shadow --float

# Custom output directory
node ./sub-packages/product-photo-maker/bin/make-product-photo.mjs -o ./my-output product.jpg --shadow

Flag Selection Guide

Choosing the right flags depends on the source image characteristics:

Background Removal Decision

  1. Product fills entire frame, no background visible--no-bg-removal
  • Example: product-only photo taken on a solid backdrop that fills edge-to-edge
  • Avoids ML model damaging product colors (especially dark/black products)
  • Fastest mode (~0.2s per image)
  1. Product on a background, product fills most of the frame--no-trim
  • Example: studio shot with small margins
  • Background removal runs but trimming is skipped to prevent overcropping
  1. Product on a background with significant space around it → default (no flags)
  • Example: product on white/gray background with padding
  • Full pipeline: bg removal → trim → resize → composite
  • ~1.5-2.5s per image (ML model processing)

Shadow Decision

Always add --shadow for product photos. Then decide float vs grounded:

  • Eurorack modular synth panels--shadow --float
    • Tall-narrow rectangles (3
      to 5
      aspect ratio)
    • Visible metal/aluminum faceplate, rows of 3.5mm jacks
    • Panel hovers above surface in real use
  • Desktop/standalone products--shadow (grounded)
    • MIDI controllers, desktop units, boxes
    • Products that sit flat on a surface

The /l-make-product-photo Claude Code skill auto-detects the product type and selects the appropriate flags. It examines the image to determine whether background removal is needed, whether the product is a modular panel or desktop unit, and groups images by flag combination for batch processing.

Background Removal Notes

The ML-based background removal (@imgly/background-removal-node) works well for most images but has a known limitation: it can create “holes” in the product for dark/black products where the model confuses product interior with background. This is especially common with dark panels that have internal dark areas. The workaround is to use --no-bg-removal for edge-to-edge product photos where no background removal is needed.

Dependencies

PackagePurpose
@imgly/background-removal-nodeML-based background removal (runs locally, no API key)
sharpImage resizing, compositing, shadow generation, and format conversion
chalkColored CLI output

The first run downloads the ML model (~30MB). Subsequent runs reuse the cached model and are faster.

API Reference

makeProductPhoto(inputPath, outputDir, options)

Core exported function for programmatic use.

Parameters:

  • inputPath (string): Path to the source image
  • outputDir (string): Output directory path (created if missing)
  • options (object):
    • noTrim (boolean, default false): Skip transparent pixel trimming
    • noBgRemoval (boolean, default false): Skip ML background removal
    • withBg (string | null, default null): Custom background image path
    • shadow (boolean, default false): Add directional shadow
    • float (boolean, default false): Use floating shadow (for Eurorack panels)

Returns: Promise<string> — path to the output JPEG file

Output filename: Same as input with .jpg extension (e.g., product.heicproduct.jpg)

generateShadowLayers(productPngBuffer, options)

Generates all shadow layers for a product image. Exported from src/shadow.mjs.

Parameters:

  • productPngBuffer (Buffer): Full-canvas (1600x1600) transparent PNG with product
  • options (object):
    • float (boolean, default false): Use floating shadow mode

Returns: Promise<Buffer[]> — array of RGBA PNG buffers to composite in order

Workflow Integration

This tool sits before the main image pipeline:

graph LR
    A[Raw Photo] -->|product-photo-maker| B[Standardized 1600x1600 JPEG]
    B -->|Copy to /imgs/| C[Image Source Directory]
    C -->|pnpm convimgs| D[WebP variants + OGP + Mercari PNG]
    D -->|pnpm r2:upload| E[Cloudflare R2 CDN]

Typical workflow:

  1. Run make-product-photo on source images
  2. Copy output to /imgs/ with appropriate naming (add __og suffix for OGP)
  3. Run pnpm convimgs to generate web variants
  4. Run pnpm r2:upload to deploy to CDN
  5. Reference image slug in product data or MDX frontmatter

Relationship to Image Processor

AspectProduct Photo MakerImage Processor
PurposePrepare raw photos into standardized product imagesGenerate web-optimized variants for the site
InputRaw camera photos (any format)Standardized images in /imgs/
OutputSingle 1600x1600 JPEG with shadowWebP variants, OGP, Mercari PNG, metadata
Pipeline positionBefore /imgs/After /imgs/
Key dependency@imgly/background-removal-node, sharpsharp (+ blurhash)