Configure all settings in one place using the configure helper:
# In test_helper.rb or rails_helper.rb
Capybara::Screenshot::Diff.configure do |screenshot, diff|
screenshot.window_size = [1280, 1024]
screenshot.stability_time_limit = 1
screenshot.blur_active_element = true
screenshot.hide_caret = true
diff.driver = :vips
diff.tolerance = 0.0005
diff.color_distance_limit = 15
endNote: fail_if_new defaults to true in CI environments (when ENV['CI'] is set). New screenshots are allowed locally but rejected in CI — no configuration needed.
Note: Setting Capybara::Screenshot.enabled = false is sufficient to disable all screenshots. There is no need to define no-op modules or monkey-patch the gem.
| Use Case | VIPS tolerance |
ChunkyPNG color_distance_limit |
stability_time_limit |
|---|---|---|---|
| Animated/complex pages | 0.01 | 30 | 2s |
| Standard Rails apps | 0.001 (default) | 15 | 1s |
| Pixel-perfect design tests | 0.0001 | 5 | 1s |
Note: VIPS defaults to tolerance: 0.001 (allows 0.1% pixel difference). ChunkyPNG has no default tolerance.
Important: perceptual_threshold, color_distance_limit, and tolerance serve different purposes. Use this decision tree:
| Method | Scale | Driver | Best for |
|---|---|---|---|
perceptual_threshold |
0-100+ (dE00) | VIPS only | Cross-OS/browser font rendering, anti-aliasing |
color_distance_limit |
0-510 (RGBA Euclidean) | VIPS, ChunkyPNG | Legacy setups, fine-grained RGB control |
Recommendation: Use perceptual_threshold: 2.0 for most cases. It matches human perception and needs less tuning.
perceptual_threshold and color_distance_limit cannot both be active — if you set both, perceptual_threshold wins and color_distance_limit is ignored. However, tolerance works with both methods and is applied by default for VIPS (0.001). This means even with perceptual_threshold: 2.0, the tolerance: 0.001 default still filters results.
| Setting | What it does | Scale |
|---|---|---|
tolerance |
Maximum allowed ratio of different pixels (VIPS) or diff bounding box (ChunkyPNG) | 0.0-1.0 |
Example: tolerance: 0.001 allows 0.1% of the image to differ (e.g., 125 pixels in a 1280×1024 screenshot).
Key difference:
perceptual_threshold/color_distance_limit→ "how different can a pixel be?"tolerance→ "how many pixels can differ?"
# Modern approach (recommended)
screenshot 'dashboard', perceptual_threshold: 2.0
# Allow small noise regions
screenshot 'dashboard', perceptual_threshold: 2.0, tolerance: 0.001
# Legacy ChunkyPNG setup
screenshot 'dashboard', color_distance_limit: 15Tier 1 — Zero config (works immediately):
blur_active_element, hide_caret, and fail_if_new (in CI) are enabled by default.
Just require 'capybara_screenshot_diff/minitest' and call screenshot.
Tier 2 — Set when tests are flaky:
| Setting | When to use |
|---|---|
window_size |
Screenshots differ between machines due to different browser sizes |
tolerance |
Sub-pixel rendering differences cause false positives |
skip_area |
Dynamic content (timestamps, ads) changes between runs |
stability_time_limit |
Animations or loading states cause inconsistent captures |
Tier 3 — Advanced tuning:
| Setting | When to use |
|---|---|
perceptual_threshold |
Anti-aliasing false positives across OS/browser versions |
shift_distance_limit |
Content shifts by a few pixels (ChunkyPNG only) |
area_size_limit |
Allow small diff regions below a pixel count |
color_distance_limit |
Fine-tune raw RGB channel tolerance |
median_filter_window_size |
Smooth noise before comparison (VIPS only) |
You can specify the desired screen size using
Capybara::Screenshot.window_size = [1024, 768]This will force the screen shots to the given size, and skip taking screen shots unless the desired window size can be achieved.
If you want to skip taking screen shots, set
Capybara::Screenshot.enabled = falseYou can of course set this by an environment variable
Capybara::Screenshot.enabled = ENV['TAKE_SCREENSHOTS']If you want to skip the assertion for change in the screen shot, set
Capybara::Screenshot::Diff.enabled = falseUsing an environment variable
Capybara::Screenshot::Diff.enabled = ENV['COMPARE_SCREENSHOTS']To allow screenshot differences, but still fail on functional errors, you can set the following option:
Capybara::Screenshot::Diff.fail_on_difference = falseIt defaults to true. This can be useful in continuous integration to a generate a screenshot difference
report while still reporting functional errors.
To fail the test if a new screenshot is taken, set the following option:
Capybara::Screenshot::Diff.fail_if_new = trueIf fail_if_new is set to true, the test will fail if a new screenshot is taken
that does not have a corresponding previous image to compare against.
This can be useful in situations where you want to ensure
that every screenshot taken by your tests corresponds to an expected state of your application.
By default, Capybara::Screenshot::Diff saves screenshots to a
doc/screenshots folder, relative to either Rails.root (if you're in Rails),
or your current directory otherwise.
If you want to change where screenshots are saved to, then there are two configuration options that that are relevant.
The most likely one you'll want to modify is ...
Capybara::Screenshot.save_path = "other/path"The save_path option is relative to Capybara::Screenshot.root.
Capybara::Screenshot.root defaults to either Rails.root (if you're in
Rails) or your current directory. You can change it to something entirely
different if necessary, such as when using an alternative web framework.
Capybara::Screenshot.root = Hanami.rootTo ensure that animations are finished before saving a screen shot, you can add a stability time limit. If the stability time limit is set, a second screen shot will be taken and compared to the first. This is repeated until two subsequent screen shots are identical.
Capybara::Screenshot.stability_time_limit = 0.1This can be overridden on a single screenshot:
test 'stability_time_limit' do
visit '/'
screenshot 'index', stability_time_limit: 0.5
endWhen the stability_time_limit is set, but no stable screenshot can be taken, a timeout occurs.
The timeout occurs after Capybara.default_max_wait_time, but can be overridden by an option.
test 'max wait time' do
visit '/'
screenshot 'index', wait: 20.seconds
endIn Chrome the screenshot includes the blinking input cursor. This can make it impossible to get a
stable screenshot. To get around this you can set the hide caret option:
Capybara::Screenshot.hide_caret = trueThis will make the cursor (caret) transparent (invisible), so the blinking does not delay the screen shot.
Another way to avoid the cursor blinking is to set the blur_active_element option:
Capybara::Screenshot.blur_active_element = trueThis will remove the focus from the active element, removing the blinking cursor.
Sometimes you want to allow small differences in the images. For example, Chrome renders the same
page slightly differently sometimes. You can set set the color difference threshold for the
comparison using the color_distance_limit option to the screenshot method:
test 'color threshold' do
visit '/'
screenshot 'index', color_distance_limit: 30
endThe difference is calculated as the euclidean distance. You can also set this globally:
Capybara::Screenshot::Diff.color_distance_limit = 42Sometimes you want to allow small movements in the images. For example, jquery-tablesorter
renders the same table slightly differently sometimes. You can set set the shift distance
threshold for the comparison using the shift_distance_limit option to the screenshot
method:
test 'color threshold' do
visit '/'
screenshot 'index', shift_distance_limit: 2
endThe difference is calculated as maximum distance in either the X or the Y axis. You can also set this globally:
Capybara::Screenshot::Diff.shift_distance_limit = 1Note: For each increase in shift_distance_limit more pixels are searched for a matching color value, and
this will impact performance severely if a match cannot be found.
If shift_distance_limit is nil shift distance is not measured. If shift_distance_limit is set,
even to 0, shift distance is measured and reported on image differences.
You can set set a threshold for the differing area size for the comparison
using the area_size_limit option to the screenshot method:
test 'area threshold' do
visit '/'
screenshot 'index', area_size_limit: 17
endThe difference is calculated as width * height. You can also set this globally:
Capybara::Screenshot::Diff.area_size_limit = 42Sometimes you have expected change that you want to ignore.
You can use the skip_area option with [left, top, right, bottom]
or css selector like '#footer' or '.container .skipped_element' to the screenshot method to ignore an area.
Be aware that if the selector is not in the page then the library will wait the default wait time for it to appear.
Therefore, it is best to only use css selectors for skip_areas you know will be in the page:
test 'unstable area' do
visit '/'
screenshot 'index', skip_area: [[17, 6, 27, 16], '.container .skipped_element', '#footer']
endThe arguments are [left, top, right, bottom] for the area you want to ignore. You can also set this globally:
Capybara::Screenshot::Diff.skip_area = [0, 0, 64, 48]If you need to ignore multiple areas:
screenshot 'index', skip_area: [[0, 0, 64, 48], [17, 6, 27, 16], 'css_selector .element']If you would like to override the screenshot method or for some other reason would like to skip stack
frames when reporting image differences, you can use the skip_stack_frames option:
test 'test visiting the index' do
visit root_path
screenshot :index
end
private
def screenshot(name, **options)
super(name, skip_stack_frames: 1, **options)
endYou can specify the format of the screenshots taken by setting the screenshot_format option. By default, the format is set to "png". However, you can change this to any format supported by your image processing driver. For example, to set the format to "webp", you can do the following:
Capybara::Screenshot.screenshot_format = "webp"Allow to bypass screenshot options to Capybara driver.
# To create full page screenshots for Selenium
Capybara::Screenshot.capybara_screenshot_options[:full_page] = true
screenshot('index', median_filter_window_size: 2, capybara_screenshot_options: {full_page: false})