Under the image sits a native <input type="range">, invisible but focusable. It gives you arrow-key support and screen-reader announcements ("40% before, 60% after") without writing any of it. Pointer dragging is handled separately with pointer events, because some mobile browsers only let you drag a range by its tiny thumb. Both paths write the same value, and the script copies it into a CSS variable.
The "before" image sits on top and is clipped with clip-path: inset(), which hides everything to the right of --position. The handle is a 2px line at the same position. touch-action: pan-y lets vertical swipes still scroll the page on phones.
Use real images: swap the two <svg> elements for <img> tags with the same classes. This demo draws one scene and shows it twice, with a grayscale filter on the "before" side, so it needs no image files.