Toolium

Image Cropping, Touch Events, and the Pointer-Events Rewrite

Hemanth Gedda6 min read

The first version of Toolium's Image Cropper used onMouseDown, onMouseMove, and onMouseUp for the crop-handle drag interactions. It worked perfectly on a desktop browser. It also worked surprisingly well on iOS Safari, because iOS synthesizes mouse events from touches in most cases. But it did not work at all on Android Chrome, and a user reported it as broken. I went to debug it and discovered that the entire mouse-event approach was wrong; the right approach was Pointer Events, which provide a unified API for mouse, pen, and touch. I rewrote the interaction layer over a weekend and the tool now works identically across input types. That story is mostly orthogonal to this post about cropping, but it is the kind of thing I am happy to have spent the time on, and it is on the list of fixes I describe on the methodology page.

Why cropping deserves more thought than it usually gets

Cropping is the cheapest and highest-leverage image edit. A photo with too much sky above the subject feels small and unimportant. The same photo cropped to put the subject closer to the rule-of-thirds intersection feels intentional. A screenshot with the browser chrome cropped off looks like a deliberate documentation asset. A square crop from a landscape photo is the difference between "Instagram post" and "Instagram post with white bars on top and bottom that look amateur."

Most people crop by vibes. The Toolium Image Cropper tries to make intentional cropping easy by giving you aspect-ratio presets that map to real destinations, plus a freeform mode for the cases where the destination dictates the rectangle. Either approach beats accidental cropping.

The aspect ratios that matter

The preset list is short and opinionated:

  • 1:1 (square). Instagram posts, profile pictures, album art. Square is the default for any platform that displays images inline alongside other content; centered subjects work best.
  • 4:5 (portrait). Instagram's other supported aspect ratio. Takes up more vertical space in the feed, which is part of why Instagram photographers use it.
  • 16:9 (widescreen). YouTube thumbnails, video frames, modern monitors, presentation slides. Subjects need to be horizontally arranged or the composition feels off.
  • 4:3 (classic). Old video, traditional camera aspect ratios, slides in older formats. Less common now but still useful for cross-medium content.
  • 3:2 (DSLR). The native aspect ratio of full-frame and APS-C cameras, and most photo prints (4x6, 8x12).
  • Freeform. No constraint. Use when the destination is unusual or you are cropping to remove specific content rather than fitting a frame.

The Pointer Events rewrite

I want to explain this in more detail because it is the kind of small fix that disproportionately affects user trust.

The original cropper attached mouse event handlers to the crop handles and the image. On a desktop, you click a corner, drag, release, and the crop box resizes. On a touchscreen, the browser tries to be helpful and synthesizes mouse events from touches, but the synthesis is not consistent across platforms. iOS does it well in Safari and Chrome. Android does it well in Chrome but with subtle differences (the synthesized events have slightly different coordinates). Older Android browsers and some embedded WebViews do not synthesize at all.

Pointer Events is a unified spec (PointerEvent) that fires for any input type - mouse, pen, touch, even some game controllers. The events carry information about the input type so you can branch behavior if needed, but the basic flow (pointerdown, pointermove, pointerup) is identical regardless of input. Rewriting the cropper to use PointerEvent took about half a day and immediately worked across every device I had access to. The lesson, generalized: if you have an interaction that involves dragging, use Pointer Events. The mouse-events approach is a 1995-era abstraction.

The actual workflow

The Image Cropper flow:

  1. Drop an image. The tool reads it and displays it at fit-to-canvas size.
  2. Pick an aspect ratio preset, or leave on "Free" for unconstrained cropping.
  3. Drag on the image to draw an initial crop rectangle. You can also start from a default centered rectangle by clicking "Reset to center."
  4. Drag the corner handles to resize. With an aspect ratio locked, both directions of resize honor the constraint.
  5. Drag the inside of the rectangle to reposition without resizing.
  6. Click "Crop and Download." The output downloads as the same format as the input.

The crop is a pixel-for-pixel extraction; no resampling is applied. If you need the output at a specific pixel size (1080x1080 for Instagram, say), crop here first, then resize with the Image Resize tool. Doing the two steps separately is more predictable than trying to combine them.

Format and quality

The output format matches the input. A JPG cropped here produces a JPG. A PNG produces a PNG. The cropped output is encoded fresh, so if you start from a JPG, you have a single round-trip of JPEG encoding (small additional loss). If you crop a PNG, the output is lossless from the cropped pixels.

For repeated cropping (cropping the result of a previous crop), this matters: every JPEG round-trip costs a little quality. If you are doing multiple edits, work in a lossless format and export to JPEG only at the end.

Circular crops, rounded corners

People ask occasionally whether the tool can produce a circular crop. It cannot, because PNG and JPG store rectangular pixel grids; there is no native "circular image" format. What apps usually do for circular avatars is crop rectangularly, then use CSS border-radius (for web display) or an alpha mask (for image files) to round the corners. For circular avatars, the workflow is: crop to a square here, then in your destination tool (CSS, Photoshop, a graphics tool) apply the rounding.

What this tool is not for

  • Batch cropping the same region from many files. The tool is one image at a time. For batch operations, ImageMagick is your friend: mogrify -crop 800x600+100+50 *.jpg crops 800x600 starting at offset (100, 50) for every JPG in the directory.
  • Cropping while applying other edits (color correction, sharpening). The cropper is single-purpose. For multi-step image edits, a real editor like Photoshop, Affinity Photo, or even Photos.app is more efficient.
  • Cropping out of a multi-frame format (GIF, multi-image WebP). The tool reads a single frame. Animated formats need a different approach.

Try the tool mentioned in this article

Open tool