╔════════════════════════════════════════════════════════════════════════════════════════════╗
║ ║
║ ██╗███████╗██╗ ██╗██╗██╗ ██╗ █████╗ ██████╗ █████╗ ║
║ ██║██╔════╝██║ ██║██║██║ ██║██╔══██╗██╔══██╗██╔══██╗ ║
║ ██║███████╗███████║██║███████║███████║██████╔╝███████║ ║
║ ██║╚════██║██╔══██║██║██╔══██║██╔══██║██╔══██╗██╔══██║ ║
║ ██║███████║██║ ██║██║██║ ██║██║ ██║██║ ██║██║ ██║ ║
║ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ║
║ ║
║ 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 0–9 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.
Metadata
Release files for ishihara 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ishihara-0.1.2.tar.gz | 12.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ishihara-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.1 kB
Release files / ishihara-0.1.2.tar.gz
| Download URL | ishihara-0.1.2.tar.gz |
|---|---|
| Size | 12.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
43b5b31d162610e97aa41d2b3a3a4e7e1947ba0eb36f3df205d4a3a311107cc4
|
|
BLAKE2b-256 checksum How to use checksums |
1092d7fc540b302efa02281e4fbe8c1cff73d4fba03b514eb2ae1c3a1911ca5d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.14
|
Release files / ishihara-0.1.2-py3-none-any.whl
| Download URL | ishihara-0.1.2-py3-none-any.whl |
|---|---|
| Size | 11.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fc1a0616592663e65b926dd20b389b567e945ca10b4a15a8771cec7b883805f0
|
|
BLAKE2b-256 checksum How to use checksums |
64877cfd794e667f112a4bec6fbdff214d9266cadee22ac24857b3237aafe313
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.14
|