Skip to main content
╔════════════════════════════════════════════════════════════════════════════════════════════╗
║                                                                                            ║
║                   ██╗███████╗██╗  ██╗██╗██╗  ██╗ █████╗ ██████╗  █████╗                    ║
║                   ██║██╔════╝██║  ██║██║██║  ██║██╔══██╗██╔══██╗██╔══██╗                   ║
║                   ██║███████╗███████║██║███████║███████║██████╔╝███████║                   ║
║                   ██║╚════██║██╔══██║██║██╔══██║██╔══██║██╔══██╗██╔══██║                   ║
║                   ██║███████║██║  ██║██║██║  ██║██║  ██║██║  ██║██║  ██║                   ║
║                   ╚═╝╚══════╝╚═╝  ╚═╝╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝                   ║
║                                                                                            ║
║                       pseudoisochromatic plates, one seed at a time                        ║
║                                                                                            ║
╚════════════════════════════════════════════════════════════════════════════════════════════╝

ishihara

Generate pseudoisochromatic colour vision plates — a figure hidden in a field of dots, readable only if you can separate two hues. No dependencies, output is SVG, and every plate is a pure function of its seed.

pip install ishihara
from ishihara import generate

generate("74", axis="rg", seed=1).save_svg("plate.svg")
ishihara 74 --axis rg --seed 1 -o plate.svg
ishihara --set --seed 100 -o plates/

Three axes: rg (red-green), tritan (blue-yellow), and control, which separates figure from ground by lightness alone and is therefore readable by every vision type including total colour blindness.

Why the dots

The dots are the mechanism, not decoration. Varying their size and lightness strips out every cue except hue, so the figure cannot be found by shape, edge or brightness. Packing is dart-throwing against a spatial hash rather than a lattice, because a regular grid gives the eye a texture to lock onto, and each dot draws its colour from a range rather than a fixed value — a figure painted one flat colour reads as a shape with an edge.

The seed

Plates in the wild are generated with an unseeded random number generator, which is fine for a screening tool and useless for anything you have to reproduce. Passing seed makes the plate deterministic: same seed, same dots, same colours, forever. Omit it and you get a different plate every call.

A palette can be wrong in a way that looks right

The tritan palette here replaced one that measured 52.0 degrees off the tritan confusion axis. It separated figure from ground across the confusion line instead of along it: mean ΔE2000 of 35.1 to normal vision and 52.4 under tritan simulation. A tritan viewer saw the digit more clearly than someone with typical colour vision — the plate scored backwards, and it looked completely normal to the eye.

The pair now shipped is cyan on green: 33.5 to normal vision, 9.0 under tritan (below the collapse threshold), 37.0 under deutan, so it does not accidentally screen red-green as well.

That is the argument for measuring plates rather than choosing them. If you build your own palette, measure it against the confusion axis before trusting it.

Custom figures

Digits 09 ship as bitmap glyphs, so there is no font dependency. Anything else goes in as a mask — a callable taking pixel coordinates and returning whether that point is inside the figure:

from ishihara import generate

def circle(x, y):
    return (x - 350) ** 2 + (y - 350) ** 2 < 120 ** 2

generate(mask=circle, axis="rg", seed=7).save_svg("dot.svg")

Checking a plate

For simulating colour vision deficiency, measuring ΔE2000 between two colours, and testing whether a palette survives protan, deutan and tritan, see opticquiz-cvd, which this package lists as an optional extra:

pip install "ishihara[check]"

What this is not

Not a diagnostic. A generated plate set screens; it does not diagnose, and a result from a screen belongs with an optometrist rather than in a conclusion. Display calibration, ambient light and screen gamut all move the answer, and none of them are controlled here.

Licence

MIT.


╔════════════════════════════════════════════════════════════╗
║                                                            ║
║      ███████╗      ██╗  ██╗███████╗██╗   ██╗███████╗       ║
║      ██╔════╝      ██║ ██╔╝██╔════╝╚██╗ ██╔╝██╔════╝       ║
║      █████╗  █████╗█████╔╝ █████╗   ╚████╔╝ ███████╗       ║
║      ██╔══╝  ╚════╝██╔═██╗ ██╔══╝    ╚██╔╝  ╚════██║       ║
║      ██║           ██║  ██╗███████╗   ██║   ███████║       ║
║      ╚═╝           ╚═╝  ╚═╝╚══════╝   ╚═╝   ╚══════╝       ║
║                                                            ║
║               ·   C  R  E  A  T  I  V  E   ·               ║
║                                                            ║
║          ────────────────────────────────────────          ║
║                                                            ║
║                      Vincent Gonzalez                      ║
║                         f-keys.com                         ║
║                 ORCID 0009-0005-3640-014X                  ║
║                                                            ║
╚════════════════════════════════════════════════════════════╝

Part of F-Keys — independent hardware, software and internet products. See the working log and live status.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ishihara-0.1.1.tar.gz (12.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ishihara-0.1.1-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

Details for the file ishihara-0.1.1.tar.gz.

File metadata

  • Download URL: ishihara-0.1.1.tar.gz
  • Upload date:
  • Size: 12.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for ishihara-0.1.1.tar.gz
Algorithm Hash digest
SHA256 96d17a002a29b2e778a489016b9783e6e3ef9aa20cb76fa8f74c4fcd47cb78dd
MD5 5a88d12da59418169597fb8b5dbd8f77
BLAKE2b-256 be374f704fd3ff663a12127332bf0d52d56c3a0950372c61ee1908d3f647fd32

See more details on using hashes here.

File details

Details for the file ishihara-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: ishihara-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 11.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for ishihara-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 62ba3407ffec1cda8fe6bc4c874c592c1c62604a6c4e61ecd9e8ba1dbd60b7e2
MD5 e4a8626f7dcf192597a8c117735c59cd
BLAKE2b-256 f0d34d69f6e1d7cf4dc3eba1333d3782d781a51b6b4654829ab68c534def2f69

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.2

2 files

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page