Skip to content

Proposal: Move image processing out of Baileys core; allow caller-provided thumbnail/profile-picture processor #2597

Description

@steipete

Hi! I would like to propose moving image processing out of Baileys core so Baileys no longer imports image libraries such as Sharp or Jimp itself.

Current behavior

Baileys currently dynamically imports sharp and jimp in src/Utils/messages-media.ts / lib/Utils/messages-media.js, prefers Sharp when present, falls back to Jimp, and throws when neither is available.

The actual image operations Baileys needs are narrow:

  • image message thumbnails: resize to small JPEG, default width 32, quality 50
  • link preview thumbnails: resize to JPEG, default width 192
  • profile picture updates: resize to JPEG, default 640x640, quality 50
  • image thumbnails also return original width/height when available

For applications that already have their own image pipeline, pulling another image library only for these small transformations is expensive. In our install, the sizes are roughly:

  • Sharp active macOS arm64 stack: about 16 MB
    • sharp: 596 KB
    • @img/sharp-darwin-arm64: 280 KB
    • @img/sharp-libvips-darwin-arm64: 15 MB
  • Jimp stack: about 102 MB
    • jimp: 3.3 MB
    • @jimp/*: 88 MB
    • codec/plugin deps such as gifwrap, image-q, pngjs, jpeg-js, omggif, utif2, etc.
  • Another image library already used by the application, @silvia-odwyer/photon-node, is about 2.2 MB.

Jimp is especially problematic here: although it avoids native dependencies, its full plugin/codec dependency tree is very large, and it is only needed as Baileys' fallback for generating tiny JPEG thumbnails/profile pictures.

Node does not provide enough built-in image processing for this

For these operations, Baileys really does need either an image library or a caller-provided implementation. Node core does not provide a portable built-in JPEG/PNG/WebP decode + resize + JPEG encode pipeline. So the clean split seems to be:

  • Baileys core: no image library imports
  • application: provides image processing when it wants generated thumbnails/profile pictures
  • no processor: Baileys skips optional generated thumbnails, or throws a targeted error only for operations that truly require processing

Proposal

Add a small optional image processor interface and stop auto-importing Sharp/Jimp from Baileys core.

Possible API shape:

export type BaileysImageProcessor = {
  makeImageThumbnail(input: Buffer | string | Readable, options?: {
    width?: number;
    quality?: number;
  }): Promise<{
    jpeg: Buffer;
    original?: { width?: number; height?: number };
  }>;

  makeProfilePicture(input: Buffer | string | Readable, options?: {
    width?: number;
    height?: number;
    quality?: number;
  }): Promise<{
    jpeg: Buffer;
  }>;
};

Then expose it on socket config and/or message generation options:

makeWASocket({
  ...config,
  imageProcessor,
});

await sock.sendMessage(jid, content, {
  imageProcessor,
});

Suggested behavior without a processor

  • If an image/video message already has jpegThumbnail, use it as today.
  • If an image/link preview has no jpegThumbnail and no imageProcessor, send without an auto-generated thumbnail.
  • If width/height are provided by the caller, use them.
  • If dimensions are not provided, either omit them or use cheap header parsing where Baileys already has enough data; avoid pulling a full image library just for dimensions.
  • For updateProfilePicture, either accept an already-prepared JPEG or throw a targeted error such as: No imageProcessor configured; pass a processed JPEG or configure imageProcessor.

Upgrade path

To avoid surprising existing users, this could be staged:

  1. Add imageProcessor.
  2. Keep Sharp/Jimp auto-import temporarily behind a legacy compatibility option.
  3. Warn once when Baileys auto-imports Sharp/Jimp: automatic image library discovery is deprecated; pass imageProcessor instead.
  4. In the next major/stable boundary, default to no auto-imports.
  5. Remove Sharp/Jimp peer dependencies and built-in discovery.

If v7 is still allowed to make this change before stable, an even cleaner path would be to remove auto-imports now and document the optional processor contract.

Expected benefits

  • Baileys core no longer imports or peer-depends on image libraries.
  • Applications that already have image processing can use Sharp, Jimp, Photon, platform-native APIs, or their existing media pipeline.
  • Applications that do not care about generated thumbnails do not pay for image processing at all.
  • The public contract stays focused on Baileys' real needs: produce small JPEG thumbnails/profile pictures and optionally original dimensions.
  • No need to expose library-specific concepts like Sharp instances, Jimp objects, PNG handling, HEIC support, limitInputPixels, etc.

Would you be open to moving image processing behind an injected interface like this? I am happy to help with a PR if the direction sounds acceptable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions