diff --git a/.github/workflows/flake8.yml b/.github/workflows/ruff.yml
similarity index 82%
rename from .github/workflows/flake8.yml
rename to .github/workflows/ruff.yml
index 7339cce..61501e6 100644
--- a/.github/workflows/flake8.yml
+++ b/.github/workflows/ruff.yml
@@ -1,4 +1,4 @@
-name: Check Code Style - FLAKE8
+name: Check Code Style - ruff
 
 on: [push, pull_request]
 
@@ -15,7 +15,7 @@ jobs:
           # installation problems if out of date.
           python -m pip install --upgrade pip setuptools numpy
 
-          pip install flake8
-      - name: Run flake8
+          pip install ruff
+      - name: Run ruff
         run: |
-          flake8
+          ruff check
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1a7887e..ec0be6e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,14 +1,16 @@
 default_language_version:
     python: python3
 repos:
+  - repo: https://github.com/astral-sh/ruff-pre-commit
+    # Ruff version.
+    rev: v0.5.1
+    hooks:
+      # Run the linter.
+      - id: ruff
   - repo: https://github.com/ambv/black
     rev: 24.2.0
     hooks:
       - id: black
-  - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v2.0.0
-    hooks:
-      - id: flake8
   - repo: https://github.com/kynan/nbstripout
     rev: 0.7.1
     hooks:
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 1b378e6..4f757ba 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -73,9 +73,9 @@ Ready to contribute? Here's how to set up `data_prototype` for local development
 
    Now you can make your changes locally.
 
-5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox::
+5. When you're done making changes, check that your changes pass linting and the tests, including testing other Python versions with tox::
 
-    $ flake8 data_prototype tests
+    $ ruff check
     $ python setup.py test
     $ tox
 
diff --git a/data_prototype/image.py b/data_prototype/image.py
index 746345c..005c76f 100644
--- a/data_prototype/image.py
+++ b/data_prototype/image.py
@@ -11,14 +11,14 @@
 
 def _interpolate_nearest(image, x, y):
     magnification = 1  # TODO
-    l, r = x
-    width = int(((round(r) + 0.5) - (round(l) - 0.5)) * magnification)
+    lef, rig = x
+    width = int(((round(rig) + 0.5) - (round(lef) - 0.5)) * magnification)
 
-    xpix = np.digitize(np.arange(width), np.linspace(0, r - l, image.shape[1]))
+    xpix = np.digitize(np.arange(width), np.linspace(0, rig - lef, image.shape[1]))
 
-    b, t = y
-    height = int(((round(t) + 0.5) - (round(b) - 0.5)) * magnification)
-    ypix = np.digitize(np.arange(height), np.linspace(0, t - b, image.shape[0]))
+    bot, top = y
+    height = int(((round(top) + 0.5) - (round(bot) - 0.5)) * magnification)
+    ypix = np.digitize(np.arange(height), np.linspace(0, top - bot, image.shape[0]))
 
     out = np.empty((height, width, 4))
 
diff --git a/docs/source/conf.py b/docs/source/conf.py
index c6fc468..5530ac9 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -23,6 +23,8 @@
 # sys.path.insert(0, os.path.abspath('.'))
 from pathlib import Path
 
+import data_prototype
+
 # are we running circle CI?
 CIRCLECI = "CIRCLECI" in os.environ
 
@@ -64,21 +66,6 @@
 
 # Sphinx gallery configuration
 
-
-def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf, **kwargs):
-    """
-    Reduce srcset when creating a PDF.
-
-    Because sphinx-gallery runs *very* early, we cannot modify this even in the
-    earliest builder-inited signal. Thus we do it at scraping time.
-    """
-    from sphinx_gallery.scrapers import matplotlib_scraper
-
-    if gallery_conf["builder_name"] == "latex":
-        gallery_conf["image_srcset"] = []
-    return matplotlib_scraper(block, block_vars, gallery_conf, **kwargs)
-
-
 sphinx_gallery_conf = {
     "examples_dirs": [
         "../../examples",
@@ -89,11 +76,10 @@ def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf, **kwargs):
     "reference_url": {
         "matplotlib": None,
     },
-    "backreferences_dir": Path("api") / Path("_as_gen"),
+    "backreferences_dir": Path("api", "_as_gen"),
     "remove_config_comments": True,
     "min_reported_time": 1,
     "thumbnail_size": (320, 224),
-    "image_scrapers": (matplotlib_reduced_latex_scraper,),
     # Compression is a significant effort that we skip for local and CI builds.
     "compress_images": ("thumbnails", "images") if is_release_build else (),
     "matplotlib_animations": True,
@@ -126,7 +112,6 @@ def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf, **kwargs):
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
-import data_prototype
 
 # The short X.Y version.
 version = data_prototype.__version__
diff --git a/examples/mandelbrot.py b/examples/mandelbrot.py
index 2efed36..9f447e2 100644
--- a/examples/mandelbrot.py
+++ b/examples/mandelbrot.py
@@ -27,9 +27,9 @@ def mandelbrot_set(X, Y, maxiter, *, horizon=3, power=2):
     N = np.zeros_like(C, dtype=int)
     Z = np.zeros_like(C)
     for n in range(maxiter):
-        I = abs(Z) < horizon
-        N += I
-        Z[I] = Z[I] ** power + C[I]
+        mask = abs(Z) < horizon
+        N += mask
+        Z[mask] = Z[mask] ** power + C[mask]
     N[N == maxiter] = -1
     return Z, N
 
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 0d4f0cc..dc5d42f 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -2,13 +2,13 @@
 # the documentation) but not necessarily required for _using_ it.
 codecov
 coverage
-flake8
 pytest
 sphinx
 twine
 pre-commit
 black
 nbstripout
+ruff
 # These are dependencies of various sphinx extensions for documentation.
 ipython
 matplotlib