GpxilStudio
A gpxil file holds a genome as pictures: one pixel for every base of DNA, with a page for each chromosome and each kind of information – the reference’s bases, a person’s genotype, and how sure the calls are. GpxilStudio is a workbench for these files that runs entirely in your browser. You can look at a gpxil file, check it, make one from a person’s variants, draw any part of it, print it, get FASTA and VCF back out, make pictures of a person’s own version of a gene and its protein, compare genomes, and look for patterns in them. You can do all of this by pointing and clicking in the Tools tab, by typing commands in the Terminal tab, or by writing Python in the Notebook tab.
The formats, and the tools between them
A gpxil file is in the middle. A reference genome (FASTA) and a person’s variants (VCF) go in. Pictures, books to print, pictures of genes and proteins (pxil files), and FASTA and VCF again, come out. Each arrow is a tool: click one to open it, or click a box to see what you can do with that kind of file.
What there is to try
- HG002’s chromosome 17 (
/data/examples/HG002_chr17.gpxil.tif): the genome of a real person, from the Genome in a Bottle project’s benchmark, with their genotype at all 83 million bases. Its TP53 gene is a good place to start.
- HG002’s TP53, as a window (
/data/examples/HG002_TP53.gpxil.tif): TP53 and 100,000 bases either side, cut out of chromosome 17 into a file of 76 kB that keeps the chromosome’s positions.
- The GRCh38 reference (
/data/grch38): a gpxil file for every human chromosome, read from the Gpxil browser’s web site a piece at a time, so nothing has to be downloaded first.
- Chromosome 17’s repeats and genes (
/data/examples/rmsk_chr17.txt.gz, /data/examples/chr17_genes.bed): RepeatMasker’s 170,331 repeats and Ensembl’s 1,187 protein-coding genes, to stack, count substitutions in, and add to a genome as planes of their own (Pure GA-sequences).
- The worked example of the gpxil specification: ten bases, four variants, small enough to read pixel by pixel (The codes).
Your files stay on this computer
Python and the tools run inside this web page. Your files are kept in this browser, on this computer, and are never sent anywhere. The only things the studio reads from the web are Python itself, when it first starts, and the GRCh38 reference and the gene files, which come from the Gpxil browser’s site. Privacy has the details, and Your files explains how to keep copies.
- Look inside a gpxil file, and check itWhat a file holds, its planes, its metadata, its counts, and a check against the specification
- Turn someone’s VCF into a gpxil fileA person’s variants and a reference become a gpxil file: with GRCh38 from the web, without the reference plane, as a window of just the region the VCF covers, and from a FASTA file
- Draw a gene, a region or a chromosomePictures for the screen or for print, in four views, with rows of any width or a Hilbert curve
- See a person’s own version of a proteinTP53’s gene, transcript and protein from each of HG002’s haplotypes, as pxil pictures, and their sequences back as FASTA
- Get sequences and variants back outA person’s haplotypes, their genotype in IUPAC codes and their variants, as FASTA and VCF for other programs
- Print a chromosome as a bookA PDF with one printed dot per base, page after page, in CMYK for a printer or RGB for the screen
- Do the same steps again: workflowsRun an example workflow, run it on another file, change it, and make one from the tools you’ve run
- Work with a gpxil file pixel by pixel, in PythonPlanes as NumPy arrays: read a region, decode the codes, find variants, count, compare haplotypes, draw, and write files of your own
- A whole genome without running out of memoryRegions and strips, a chromosome a piece at a time, files without the reference that borrow it, working copies and their counts, and the whole genome on your own computer
- A family: comparing genomesHG002’s two benchmarks compared base by base, then HG002 against both his parents: what each variant came from, and the few that are new
- Pure GA-sequences: looking for patternsAlbrecht-Buehler’s pure GA-sequences on chromosome 17: found, stacked, traced to the Alu elements whose tails they began as, and HG002’s substitutions in them counted against what their sequence predicts
- WorkflowsTools chained together: the editor, inputs and steps, running, the run’s folder, and the workflow file
- What’s in a gpxil filegpxil info: a file’s reference, sample, contigs and planes, or all its metadata as JSON
- Check a gpxil filegpxil validate: a file against the specification, its codes, its sidecar and its checksums
- Count what a file holdsgpxil stats: bases called, substitutions and Ti/Tv, insertions, deletions, phasing, quality and depth, for every contig or for regions
- Make a gpxil file (encode)gpxil encode: a reference, and one person’s VCF, as a gpxil file
- Make a working copy (subset)gpxil subset: some contigs, and some planes, of a gpxil file in a new and smaller file, copied without being decoded; or a region, such as a gene, cut out as a window
- Compare two genomesgpxil compare: two gpxil files of one person, base by base, the second as the truth: the same, written differently or different, with precision, recall and a picture
- A child and both parents (trio)gpxil compare --father --mother: Mendelian errors and candidate new mutations, where the child’s variants came from, and crossovers where the parents are phased
- Find runs of a compositiongpxil runs: the stretches of a genome made only of some bases – pure GA-sequences, runs of one base, AT-rich stretches – on one strand or both, as a BED file
- Stack features, lined upgpxil stack: features – runs, repeats, genes, any BED file – one a row, lined up at their starts, ends or centres on their own strands, sorted, and drawn as bases or variants, with each column’s bases as a table
- Substitutions in regions, by contextgpxil spectrum: does a person have more or fewer substitutions in some regions than their sequence leads one to expect? Observed against expected, context by context, with exact intervals, on the features’ own strands too
- Add an annotation planegpxil annotate: a copy of a gpxil file with an annotation – repeats, genes, a BED or bedGraph file – as a plane of its own, a value at every base, which the Viewer draws and the tools read
- Strand symmetry: skews in windowsgpxil skew: GC content, the GC and AT skews, and how far each strand’s k-mers are from their reverse complements’ counts (Chargaff’s second rule), in windows along a genome, as a table and a map
- Wrap a repeat at its periodgpxil period: a tandem repeat – a microsatellite, a GA-sequence – wrapped at its period, one unit a row, so the units line up and a change in one stands out; the reference beside the person’s two haplotypes, as a picture or text
- Draw a region as a picturegpxil render: a region in four views, as PNG for the screen or TIFF (RGB or CMYK) for print
- Print it as a book (PDF)gpxil book: a PDF to print with one dot per base, in CMYK or RGB
- The palettesgpxil palettes: the colours of A, C, G, T and N in each palette, for the screen and in ink
- Sequences as FASTAgpxil fasta: the reference, either haplotype, or the genotype in IUPAC codes, as FASTA
- Variants as VCFgpxil vcf: the person’s genotypes as a VCF again, for a region or every contig
- Genes, transcripts and proteins as pxil picturesgpxil2pxil: a gene, its transcripts and proteins, from the reference or a person’s haplotypes, as pxil files
- pxil pictures → FASTApxil: the sequences of pxil pictures as FASTA, or their metadata as JSON
- The gpxil formatHow a gpxil file holds a genome: contigs and planes, windows, layout, pages, strips, the sidecar, the metadata and the checksums
- The codesEvery value of the ref, gt and flags planes, and the specification’s worked example, pixel by pixel
- How a VCF becomes gpxil, and backHow each kind of call is written base by base, which call wins when they overlap, what is kept and what isn’t, and how a gpxil file becomes a VCF again
- pxil pictures of sequencesThe pxil format: one sequence as a PNG picture, one pixel a residue, with what it is in its metadata
- Big files and memoryHow the studio works with files of hundreds of megabytes in a browser tab, its limits, and how to write code that stays small
- The GRCh38 reference/data/grch38: GRCh38 as a gpxil file for each chromosome, read from the Gpxil browser; its genes; and the whole genome in one file to download
A first session
Take ten minutes to see what gpxil files are and what you can do with them. You’ll look at the genome of a real person, HG002, around the gene TP53; draw it as a picture; see how this person’s TP53 protein differs from the reference’s; and make a small gpxil file of your own. Each step has a button that does it for you.
1 · Look at a genome
The Viewer tab shows a gpxil file as pixels, one for every base. This button opens HG002’s chromosome 17 at TP53, a gene that is mutated in many cancers. HG002 is a man whose genome has been sequenced in great depth by the Genome in a Bottle project, so his genotype is known very reliably.
Each square is one base, in rows that read from left to right and from top to bottom, like a page of text. In this view, Variants, the reference’s bases are faded, and the bases where HG002 differs from the reference stand out: orange where one of his two copies differs (heterozygous), red where both do (homozygous). Grey bases weren’t called with confidence. Point at a square to see what the file holds there.
One of the red squares is at chr17:7,676,154. There, the reference has G, and both of HG002’s copies have C. This is a common variant, rs1042522, which changes amino acid 72 of the TP53 protein from proline to arginine (P72R). Try the other views in Show, such as Haplotypes, which draws his two copies of the chromosome in two rows.
2 · See what is in the file
A gpxil file is an ordinary TIFF picture with many pages: one page for each chromosome and each plane. The ref plane holds the reference’s bases, gt the person’s genotype, flags extra facts such as which calls are phased, and gq, dp and ps how good each call is. What’s in a gpxil file, in the Tools tab, lists them. Press Run. The first run starts Python in your browser, which takes a few seconds; the very first time, it also downloads about 15 MB.
Under the form, The same in the terminal, and in Python shows exactly what the tool runs. You can use it to do the same job again without the form.
3 · Draw it as a picture
Draw a region as a picture writes the same kind of picture as a PNG file, which you can download or put in a document. The button fills in TP53 and the variants view.
4 · This person’s TP53 protein
Because a gpxil file holds the person’s genotype at every base, their own version of a gene can be read from it, spliced and translated into protein. Genes, transcripts and proteins as pxil pictures does this for each of HG002’s two copies of chromosome 17. Each picture has one pixel for each amino acid, and says how the protein differs from the reference’s. For HG002, both say differs from the reference: P72R.
The genes come from the Gpxil browser’s gene files (Ensembl, GRCh38), which are read from the web as they’re needed.
5 · A gpxil file of your own
Make a gpxil file (encode) turns a reference genome (FASTA) and a person’s variants (VCF) into a gpxil file. The specification’s worked example is tiny, ten bases and four variants, so you can see every pixel of the result.
It writes ~/genomes/worked-example.gpxil.tif. Press View under the result to look at it, then try Check, which checks it against the gpxil specification.
6 · The same in the terminal
Every step above is a command, so the whole session can be run again in one go. Run in the terminal types these commands into the Terminal tab and runs them.
gpxil info /data/examples/HG002_chr17.gpxil.tif
gpxil render /data/examples/HG002_chr17.gpxil.tif chr17:7661779-7687546 -o ~/results/TP53_variants.png --mode variants --width 256 --scale 2
gpxil2pxil /data/examples/HG002_chr17.gpxil.tif TP53 --kinds protein -o ~/results/pxil/TP53
gpxil encode /data/examples/worked-example.fa -v /data/examples/worked-example.vcf -o ~/genomes/worked-example.gpxil.tif
gpxil validate ~/genomes/worked-example.gpxil.tif
7 · Keep your work
Everything you make is kept in this browser, on this computer. To take it elsewhere, use a file’s Download button, or My files → Download all my files (.zip) at the top right. Your files has the details. Where next? The walkthroughs go through each job in turn, and Work with a gpxil file pixel by pixel, in Python shows how to get at the data yourself.
Finding your way around
The studio has the help on the left and five tabs on the right. The tabs share one set of files and one Python, so what you make in one tab is there in all the others.
The five tabs
- Viewer
- Any gpxil file as pixels, one for each base: the reference, a person’s genotype and variants, and the quality of the calls. Point at a pixel to see what the file holds there. The Viewer
- Tools
- A form for each job: make a gpxil file, check it, draw it, print it, get FASTA and VCF out of it, make pictures of genes and proteins. Each form also shows the same job as a command and as Python. Workflows, in the same tab, chain tools together. The Tools tab
- Files
- Your files and the examples, with a view for each kind: a gpxil file’s contents, a picture, a PDF, or a text editor. Your files
- Notebook
- Python a cell at a time, with gpxiltools, gpxil2pxil and pxil ready to import, and NumPy for the planes. The Notebook tab
- Terminal
- A bash terminal with the commands
gpxil, gpxil2pxil and pxil, and the usual file and text commands. The terminal
The help
- Search the help with the box at the top of this pane. Titles and keywords count most, so a short word such as planes, haplotype or pxil works well.
- The book button next to the search box goes back to the list of all topics, and ← Back to the page you were reading.
- Help for the … tab, under the search box, opens the help for the tab you’re in.
- Drag the bar between the help and the tabs to make the help wider or narrower, or double-click it to put it back. The button at the top left, or Ctrl+\, hides and shows the help.
- The ▶ buttons in the help do what they say: they open a file, a tool or an example with the right settings, so you can follow a walkthrough by pressing them in turn.
- Boxes of code have their own buttons: Copy, Run in the terminal for commands, and Add to notebook for Python.
The buttons at the top
- Saved in this browser
- Your files are saved as you work. This says so, or warns you when the browser can’t keep them (in a private window, say) or they take up a lot of room. Where your files are kept
- user
- Put in your name, and the terminal shows it in place of
user. It’s kept in this browser only. Your home folder is /home/user whatever the name.
- My files
- Download all your files as a .zip, add files or a folder from your computer, see where your files are kept, or start afresh by deleting them all.
- Full screen
- Uses the whole screen (F11 does too).
On a phone or a small screen
Everything works on a narrow screen, but there’s less room. The help slides in over the tabs when you open it with the button at the top left, and slides away when you tap outside it. On a very narrow screen, the tabs other than the one you’re in show only their icons. The Viewer’s controls stack above the picture. Large jobs, such as encoding a whole chromosome, are better done on a computer, which has more memory than a phone.
Your files
Your files live in your home folder, ~ (/home/user). The Files tab shows them, with the examples and the GRCh38 reference beside them, and opens each kind of file in a view that suits it. Every tab sees the same files.
The folders
| Folder | What goes there |
~/genomes | gpxil files you make with Make a gpxil file, and files you add from your computer in the Viewer |
~/results | what the tools write: pictures, books, FASTA and VCF files, pxil pictures (in ~/results/pxil), and the runs of workflows |
~/notebooks | notebooks, including gpxil-in-python.ipynb, which a new visitor is given |
~/workflows | your workflows, made the first time you save one |
~/examples | copies of examples you ask for, which are yours to change |
/data/examples | the examples (read-only): HG002’s chromosome 17 (and a newer benchmark of it), his TP53 as a window, his father’s and mother’s (HG003 and HG004, without the reference plane), HG002’s variants around TP53 with the regions called with confidence, TP53’s transcripts, chromosome 17’s repeats (RepeatMasker) and protein-coding genes (Ensembl), the specification’s worked example, and example workflows |
/data/grch38 | the GRCh38 reference, a gpxil file for each chromosome (read-only), read from the Gpxil browser’s web site a piece at a time (The GRCh38 reference) |
You can make any other folders you like. The tools save in ~/results unless you choose otherwise, and they can only save in your home folder.
Looking at a file
Click a file in the Files tab to open it. A gpxil file shows what it holds: its reference, its sample, its layout and a table of its contigs and planes, read straight from the file without starting Python, so even a whole genome opens at once. A pxil picture is shown large, pixel by pixel, with what it says about itself – its kind, IDs and length, where it comes from, and notes such as differs from the reference: P72R – and its sequence, read from the pixels in the page; point at a pixel to see which residue it is, and its number. Other pictures and PDFs show as they are, and text files (FASTA, VCF, BED, GTF, scripts) open in an editor: change them and press Save (Ctrl+S). Above each file, buttons offer what you can do next:
| Kind of file | Buttons |
| gpxil | View (the Viewer), What’s in it (gpxil info), Check (gpxil validate), Genes as pictures (gpxil2pxil), More… (every tool that reads it) |
| pxil | As FASTA (pxil); under the picture, Copy the sequence too |
| VCF | Encode as gpxil, with this file as the person’s variants |
| FASTA | Encode as gpxil, with this file as the reference |
| workflow | Run, and Edit (or Edit a copy of an example) |
a workflow’s run (workflow-run.json) | View the run: what each step did |
Right-click a file, or use its ⋯ button, to rename, duplicate, download or delete it, or to copy its path. On an example, Copy to my files makes a copy you can change.
Adding files from your computer
Drag files onto the Files tab, or use its ↑ button, or My files → Add files from my computer… at the top. In the Viewer, Add a file… puts a gpxil file into ~/genomes and opens it. A file you add is kept in the browser as it is, and isn’t read until a tool needs it, so adding a big gpxil file is quick.
Where your files are kept
Your files are kept in this browser’s storage for this web site (IndexedDB), on this computer. They’re still there when you come back, unless you clear the browser’s data for the site. They aren’t sent anywhere, and nobody else can see them. Some things to know:
- In a private or incognito window, the browser forgets everything when the window closes. The indicator at the top says when files aren’t being kept.
- Browsers limit how much a site may keep, usually to a share of the free disk space. The indicator warns you when your files take up more than 2 GB. My files → Where are my files kept? shows how much is used.
- A single file larger than 1 GB is there while the page is open but isn’t kept, so download it before you leave.
- Your files are kept in this browser on this computer. Another browser, or another computer, has its own empty home folder.
Taking files elsewhere
- A file’s Download button saves it to your computer.
- My files → Download all my files (.zip) saves your whole home folder in one .zip file.
- In the terminal,
download FILE or download FOLDER does the same (a folder as a .zip).
Big files and Python
gpxil files are big: a chromosome is tens of megabytes, a whole genome nearly a gigabyte. The studio never copies a file of 8 MB or more into Python’s memory. Python reads it where the browser keeps it, or from the web, a piece at a time, as it needs it. A big file that a tool writes goes the other way: it is handed to the browser to keep, and Python’s copy is freed. Big files and memory explains how this works and how to make the most of it.
Starting afresh
My files → Start afresh deletes every file in your home folder from this browser, after asking you to confirm. Download what you want to keep first. The examples and the reference aren’t affected, and you are given the starting notebook again.
Walkthroughs
Each walkthrough takes you through one job from beginning to end, with a button for every step. They use the examples – HG002’s chromosome 17 and his parents’, his variants around TP53, chromosome 17’s repeats and genes, the GRCh38 reference and the specification’s worked example – so you can follow one through before you try it with files of your own.
- Look inside a gpxil file, and check itWhat a file holds, its planes, its metadata, its counts, and a check against the specification
- Turn someone’s VCF into a gpxil fileA person’s variants and a reference become a gpxil file: with GRCh38 from the web, without the reference plane, as a window of just the region the VCF covers, and from a FASTA file
- Draw a gene, a region or a chromosomePictures for the screen or for print, in four views, with rows of any width or a Hilbert curve
- See a person’s own version of a proteinTP53’s gene, transcript and protein from each of HG002’s haplotypes, as pxil pictures, and their sequences back as FASTA
- Get sequences and variants back outA person’s haplotypes, their genotype in IUPAC codes and their variants, as FASTA and VCF for other programs
- Print a chromosome as a bookA PDF with one printed dot per base, page after page, in CMYK for a printer or RGB for the screen
- Do the same steps again: workflowsRun an example workflow, run it on another file, change it, and make one from the tools you’ve run
- Work with a gpxil file pixel by pixel, in PythonPlanes as NumPy arrays: read a region, decode the codes, find variants, count, compare haplotypes, draw, and write files of your own
- A whole genome without running out of memoryRegions and strips, a chromosome a piece at a time, files without the reference that borrow it, working copies and their counts, and the whole genome on your own computer
- A family: comparing genomesHG002’s two benchmarks compared base by base, then HG002 against both his parents: what each variant came from, and the few that are new
- Pure GA-sequences: looking for patternsAlbrecht-Buehler’s pure GA-sequences on chromosome 17: found, stacked, traced to the Alu elements whose tails they began as, and HG002’s substitutions in them counted against what their sequence predicts
You’ll also find most of these jobs on the first page of the Tools tab, under Common tasks, each with its tools in the order you need them.
Look inside a gpxil file, and check it
Someone has sent you a gpxil file, or you’ve made one. This walkthrough shows what it holds – its reference, its sample, its contigs and planes, its metadata – and how to check that it follows the gpxil specification. It uses HG002’s chromosome 17; with a file of your own, choose it instead.
1 · A first look, in the Files tab
Click a gpxil file in the Files tab. Without starting Python, the tab reads the file’s first page, where its metadata is, and shows what it holds: the reference it was made from, the sample, the layout, and a table of its contigs and their planes.
2 · Its pixels, in the Viewer
The Viewer shows the planes themselves. Choose a region and what to show; point at a pixel to see the values of every plane there.
3 · What’s in it, as gpxil says it
What’s in a gpxil file runs gpxil info. It gives the file’s format version, what wrote it and when, its reference and sample, and for each contig its length, its planes, how many VCF records went into it and how many entries its sidecar has (the inserted bases and complex variants that don’t fit in a pixel).
Tick All of its metadata, as JSON to see everything the file says about itself: each contig’s checksums (md5 and refget), the pages that hold its planes, how many bases were called with confidence, and the counts the encoder kept as it read the VCF, such as conflicts and ref_mismatches (both 0 here, as they should be). The gpxil format describes every key.
4 · Count what it holds
Count what a file holds runs gpxil stats: how much of each contig, or of a region, was called with confidence, and the person’s substitutions (with their ratio of transitions to transversions, Ti/Tv), insertions and deletions, how much is phased, and the quality and depth of the calls. HG002’s chromosome 17 is 86.9 % called, with 54,935 heterozygous and 34,713 homozygous substitutions and a Ti/Tv of 2.36. The Viewer counts the same for the region in view, under its key.
5 · Check it
Check a gpxil file runs gpxil validate. It checks the metadata against the specification’s JSON schema; that every contig has the pages its metadata names, of the right size and type; that every value of every plane is a code the specification allows; that the flags, phase sets and sidecar agree with each other; and, if you leave Recompute the sequences’ checksums ticked, that the reference plane’s bases give the MD5 and refget checksums in the metadata. That last check proves the bases are those of the reference the file names.
It should say Valid. An error means a reader can’t trust the file; a warning is worth reading but doesn’t make the file invalid. A file that isn’t gpxil at all, such as an ordinary TIFF picture, fails at once: its first page has no gpxil metadata.
6 · A file on the web
The files of the GRCh38 reference in /data/grch38 are on the Gpxil browser’s web site. Looking inside one reads only its first page, so it is as quick as a file of your own. Checking its checksums reads every base, which for chromosome 1 means downloading 67 MB, so untick Recompute the sequences’ checksums for a quick check of a big file on the web.
The same in the terminal
gpxil info /data/examples/HG002_chr17.gpxil.tif
gpxil info --json /data/examples/HG002_chr17.gpxil.tif > ~/results/HG002_chr17.metadata.json
gpxil stats /data/examples/HG002_chr17.gpxil.tif
gpxil validate /data/examples/HG002_chr17.gpxil.tif
gpxil info /data/grch38/chr1.gpxil.tif
gpxil validate /data/grch38/chrM.gpxil.tif
gpxil info --json prints the metadata as JSON; here it is saved in a file, which the Files tab shows neatly. gpxil validate --no-digests skips the checksums.
The same in Python
In a notebook, the metadata is a Python dictionary, and each contig an object with its name, length and planes:
from gpxiltools import GpxilFile
from gpxiltools.validate import validate
g = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")
print(g.metadata["reference"])
print(g.metadata["sample"])
for c in g:
print(c.name, f"{c.length:,} bases", c.planes, c.entry["md5"])
report = validate("/data/examples/HG002_chr17.gpxil.tif")
print("valid" if report.ok else "not valid", report.errors, report.warnings)
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- refget sequence identifiers
- Yates AD, Adams J, Chaturvedi S, Davies RM, Laird M, Leinonen R, et al. (2021) Refget: standardized access to reference sequences. Bioinformatics 38(1): 299–300. doi:10.1093/bioinformatics/btab524
Turn someone’s VCF into a gpxil file
A VCF file lists where a person differs from the reference. A gpxil file holds the whole of each chromosome, base by base, with the person’s genotype at every base. This walkthrough makes one from HG002’s variants around TP53, with chromosome 17 of GRCh38 as the reference, read from the web. Then it makes the same file without the reference’s bases, which is a hundred times smaller, then a window of just the stretch the VCF covers, in a second, and finally the specification’s worked example from a FASTA file.
What you need
- The person’s variants: a VCF file (
.vcf or .vcf.gz) with their genotypes. A VCF of several people is fine: you choose which one to encode.
- The reference the variants were called against: a FASTA file, or a gpxil file with its reference plane, such as the chromosomes of GRCh38 in
/data/grch38. Contig names are matched with or without chr, so 17 in a VCF finds chr17.
- If you have one, a BED file of the regions called with confidence. Benchmark VCFs such as Genome in a Bottle’s come with one, and so do many variant callers’ outputs (as a gVCF, or a coverage BED).
1 · The variants
HG002_TP53.vcf holds the 206 records of the Genome in a Bottle benchmark (version 4.2.1) for HG002 between chr17:7,600,001 and chr17:7,750,000, around TP53. Open it to see what a VCF looks like: one line for each place where HG002 differs from the reference, with his genotype, such as 0/1 (one copy) or 1/1 (both).
2 · Why the regions called with confidence matter
A VCF lists the bases where a person differs from the reference, but says nothing about the rest. Some of the rest is like the reference; some wasn’t sequenced well enough to tell. Without more information, the encoder can only assume the person has the reference everywhere else – across the whole of chromosome 17, in this case, which this VCF can’t possibly tell us.
HG002_TP53.bed lists the 52 stretches, 140,254 bases in all, where the benchmark is confident of HG002’s genotype. With it, bases outside those stretches become UNKNOWN (no call) rather than the reference, so the gpxil file says exactly what is known and no more.
3 · Encode it
Make a gpxil file (encode) with the VCF, the BED file and chromosome 17 of GRCh38. The reference file holds only chromosome 17, so only chromosome 17 is encoded; with a reference of the whole genome, you’d put chr17 in Only these contigs.
Press Run. Python reads chromosome 17’s 83 million bases from the web (about 23 MB), applies each of the 206 records base by base, and writes ~/genomes/HG002_TP53.gpxil.tif: about 23 MB, almost all of it the reference plane. It takes about a minute on a desktop computer, longer on a phone, and about half a gigabyte of memory, because the encoder holds the whole chromosome’s planes while it works (the same command takes about 12 seconds on a computer’s own Python). The note under the result says how many contigs and bases it wrote.
The settings you’ll most often change:
- Planes from the VCF
- The genotype and flags planes (
gt, flags) are all that sequences, pictures and VCF need. A VCF’s genotype qualities, read depths and phase sets can go in too (gq, dp, ps), but they take as much memory again.
- Sex chromosomes
- For a man’s VCF, XY gives chromosomes X and Y one copy outside the pseudo-autosomal regions, so his second haplotype is ABSENT there rather than a copy of the first.
- Sample
- The VCF column to encode, if it has several.
Make a gpxil file describes every setting.
4 · Check it and look at it
In the Viewer, the 150,000 bases around TP53 are faded reference where they were called with confidence, with HG002’s variants in orange and red, and grey (unknown) between and around the stretches of the BED file – as is the whole of the rest of the chromosome.
5 · The same, without the reference
The reference plane takes up nearly all of the file, and it is the same for everyone. Encode again without it, with Leave out the reference plane ticked: the file, ~/genomes/HG002_TP53.noref.gpxil.tif, is about 0.2 MB instead of 23. It names its reference by the checksums of its sequences, so the reference can be found and checked later.
The button ticks Leave out the reference plane for you, and the name under Save as changes to match; press Run. Anything that needs the bases themselves then needs the reference too. The tools that read bases show a field for it, Its reference, and fill in the GRCh38 chromosome from /data/grch38; gpxil checks it against the file’s checksums. The Viewer does the same by itself for a file made on GRCh38. Use files without the reference to keep many people’s genomes small, or to send one; keep the reference with the file when it has to stand on its own.
6 · Just the stretch the VCF covers: a window
The VCF covers 150,000 of chromosome 17’s 83 million bases, so nearly all of the file you made says UNKNOWN. Give the region instead, in Only these regions, as windows: the file then holds just those bases, as a window – a contig that keeps the chromosome’s positions, so chr17:7,676,154 is still chr17:7,676,154 in the Viewer and every tool.
Press Run. Only the records that reach into the region are applied, and only the reference’s bases there are read, so it takes a second or two and little memory, and writes ~/genomes/HG002_TP53_chr17_7600001-7750000.gpxil.tif: 50 kB, with the reference’s bases (7 kB without them). It holds just what the same stretch of the whole chromosome’s file holds, base for base. A window is also the way to keep a gene from a big file: Make a working copy (subset) cuts one out of a file you have, and the Viewer’s Save this region as a gpxil file keeps what it shows.
7 · From a FASTA file: the worked example
The specification’s worked example is a reference of ten bases (worked-example.fa) and four variants (worked-example.vcf). Encode it with 4 bases per row, as in the specification, and each plane is a picture of 3 rows of 4 pixels, which you can read pixel by pixel in The codes.
The button sets Bases per row of each page to 4. A FASTA file is read whole, one record at a time, so for a whole human genome use a gpxil reference, or work on your own computer.
Your own data
- Add your VCF, and its BED file, with the Files tab’s ↑ button, or by dragging them onto it. They go into the folder you choose, such as
~/genomes.
- For a VCF on GRCh38, give the chromosome you want from
/data/grch38 as the reference, and encode one chromosome at a time. Each takes about 6 bytes of memory a base: half a gigabyte for chromosome 17, about 1.5 GB for chromosome 1, which is more than a browser tab may have. For the genes you care about, windows are quicker and smaller.
- For the biggest chromosomes, or a whole genome in one file, work on your own computer, where gpxil can use the whole-genome reference file from the Gpxil browser’s downloads (The GRCh38 reference, On your own computer).
The same in the terminal
gpxil encode /data/grch38/chr17.gpxil.tif -v /data/examples/HG002_TP53.vcf --callable /data/examples/HG002_TP53.bed --planes gt,flags -o ~/genomes/HG002_TP53.gpxil.tif
gpxil validate ~/genomes/HG002_TP53.gpxil.tif
gpxil encode /data/grch38/chr17.gpxil.tif -v /data/examples/HG002_TP53.vcf --callable /data/examples/HG002_TP53.bed --planes gt,flags --no-ref -o ~/genomes/HG002_TP53.noref.gpxil.tif
gpxil encode /data/grch38/chr17.gpxil.tif -v /data/examples/HG002_TP53.vcf --callable /data/examples/HG002_TP53.bed --planes gt,flags --region chr17:7,600,001-7,750,000 -o ~/genomes/HG002_TP53_chr17_7600001-7750000.gpxil.tif
ls -l ~/genomes
gpxil fasta ~/genomes/HG002_TP53.noref.gpxil.tif --which 1 --region chr17:7676101-7676200 --reference /data/grch38/chr17.gpxil.tif
The last command needs the reference to spell out HG002’s first haplotype from the file without one.
The same in Python
from gpxiltools.encode import encode
summary = encode("/data/examples/worked-example.fa", "/home/user/genomes/worked-example.gpxil.tif",
vcf="/data/examples/worked-example.vcf", width=4)
print(summary["contigs"], "contig,", summary["bases"], "bases,", summary["bytes"], "bytes")
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
- The GRCh38 reference genome
- Schneider VA, Graves-Lindsay T, Howe K, Bouk N, Chen HC, Kitts PA, et al. (2017) Evaluation of GRCh38 and de novo haploid genome assemblies demonstrates the enduring quality of the reference assembly. Genome Research 27(5): 849–864. doi:10.1101/gr.213611.116
- VCF, the variant call format
- Danecek P, Auton A, Abecasis G, Albers CA, Banks E, DePristo MA, et al. (2011) The variant call format and VCFtools. Bioinformatics 27(15): 2156–2158. doi:10.1093/bioinformatics/btr330
- GA4GH and the SAMtools team (hts-specs). The Variant Call Format specification, VCFv4.4. samtools.github.io/hts-specs/VCFv4.4.pdf
Draw a gene, a region or a chromosome
A gpxil file is already a picture: each base is a pixel. Drawing a region means choosing which bases, how to colour them and how to lay them out. This walkthrough draws HG002’s TP53 in each of the four views, a satellite repeat that appears only when the rows are the right width, and a whole chromosome, the mitochondrion’s.
1 · Find the region in the Viewer
The Viewer is the quickest way to find a region worth drawing. Type a region such as chr17:7,661,779-7,687,546 (TP53), move along with the arrows, and zoom with + and −. Under the picture, the Viewer gives the gpxil render command that draws what you see as a file, and Save the picture saves the view as it is.
Regions count from 1 and include both ends, as in a genome browser: chr17:7,661,779-7,687,546 is 25,768 bases. A contig’s name alone means all of it.
2 · Draw it
Draw a region as a picture writes the region as a PNG (or a TIFF). For a region the size of a gene, it suggests 256 bases a row and 4 pixels a base, so each base is a square you can see.
3 · The four views
| Show | What each pixel is |
| reference | the reference’s base, in the palette’s colours. Bases in soft-masked repeats (lower case in the reference) are paler, an unknown base (N) is near-black, in the classic colours, and a gap is paper. |
| genotype | the person’s two bases at once: where both copies agree, their base’s colour; where they differ, a mix of the two colours. A base deleted from both copies is paper. |
| haplotypes | the two copies separately, in two rows of pixels for each row of bases: the first haplotype above the second. With Mark where insertions are, a base that an insertion follows is magenta. |
| variants | the reference, faded, with the person’s variants on top: heterozygous orange, homozygous red, a deletion dark, a base an insertion follows green, no call grey, a complex variant purple. |
The Colours are one of gpxil’s palettes: classic (A red, C blue, G green, T yellow), okabe-ito, whose colours people with colour-blindness can tell apart, gpxi, the greys of Albrecht-Buehler’s original genome pixel images, and process, the four printing inks. The palettes gives their exact colours.
4 · The width of the rows
The width of the rows changes nothing in the data, only how it is laid out, but it can change what you see. A repeat whose unit is as long as a row lines up in columns. Near the middle of chromosome 17 lies alpha satellite, the repeat that makes up centromeres, in units of 171 bases. At 171 bases a row it shows as vertical stripes; at 200 it looks like noise.
In the form, set Bases per row to 171, then to 200, and compare the two pictures. This is how Albrecht-Buehler found repeats by eye: by wrapping a sequence at the width of its period.
5 · A Hilbert curve
In rows, bases that are next to each other in the genome can end up far apart in the picture: the last base of one row and the first of the next. A Hilbert curve layout folds the sequence into a square so that neighbours stay neighbours, and any stretch of the sequence is a compact blob. A gene shows as a patch rather than a band.
(Choose a Hilbert curve under Layout.) TP53’s 25,768 bases fill a square of 256 by 256 pixels, with room to spare.
6 · A whole chromosome
The mitochondrial chromosome is small enough to draw whole: 16,569 bases, its name alone as the region.
In the browser, a picture may have up to 25 million bases and 64 million pixels: a region of any chromosome, but not a large chromosome in one go (chromosome 21, the shortest autosome, has 46.7 million bases). To see a whole chromosome at one dot a base, print it as a book (Print a chromosome as a book); on your own computer, gpxil render has no such limit.
7 · For print
A PNG is for the screen. For print, choose TIFF in CMYK under Picture: each base’s colour is then given directly in ink, in the palette’s own CMYK values, rather than converted from screen colours. Give the Dots per inch you will print at, and with one pixel a base, each base is one printed dot.
The same in the terminal
gpxil render /data/examples/HG002_chr17.gpxil.tif chr17:7661779-7687546 -o ~/results/TP53_variants.png --mode variants --width 256 --scale 4
gpxil render /data/examples/HG002_chr17.gpxil.tif chr17:7661779-7687546 -o ~/results/TP53_haplotypes.png --mode haplotypes --width 256 --scale 4 --mark-insertions
gpxil render /data/examples/HG002_chr17.gpxil.tif chr17:7661779-7687546 -o ~/results/TP53_hilbert.png --mode variants --layout hilbert --scale 2
gpxil render /data/grch38/chr17.gpxil.tif chr17:22815001-22835000 -o ~/results/alpha_171.png --mode reference --width 171 --scale 3
gpxil render /data/grch38/chr17.gpxil.tif chr17:22815001-22835000 -o ~/results/alpha_200.png --mode reference --width 200 --scale 3
gpxil render /data/grch38/chrM.gpxil.tif chrM -o ~/results/chrM.png --mode reference --width 128 --palette okabe-ito
gpxil render /data/examples/HG002_chr17.gpxil.tif chr17:7661779-7687546 -o ~/results/TP53_print.tif --mode genotype --width 256 --cmyk --dpi 300
The same in Python
render returns the picture as palette indices, with its screen and print colour tables, so you can scale it, save it, or look at it in a notebook:
from gpxiltools import GpxilFile
from gpxiltools.render import render
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
picture = render(g["chr17"], mode="variants", start=7_661_778, end=7_687_546, bases_per_row=256)
picture = picture.scaled(2)
picture.save_png("/home/user/results/TP53_from_python.png")
print(picture.shape, picture.title)
for label, index in picture.legend:
print(label, picture.rgb[index])
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome pixel images (GPxI)
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- The Hilbert-curve layout
- Anders S (2009) Visualization of genomic data with the Hilbert curve. Bioinformatics 25(10): 1231–1235. doi:10.1093/bioinformatics/btp152
- The Okabe–Ito colours
- Okabe M, Ito K (2002) Color Universal Design (CUD): how to make figures and presentations that are friendly to colorblind people. jfly.uni-koeln.de/color
See a person’s own version of a protein
A gpxil file holds both copies of each of a person’s chromosomes, base by base, so their own versions of a gene can be spelt out, spliced into transcripts and translated into proteins. This walkthrough does it for TP53 in HG002, whose two copies of the TP53 protein both differ from the reference’s at amino acid 72. The results are pxil files: pictures with one pixel for each base or amino acid, which hold the sequence exactly.
1 · The gene and its transcripts
TP53 lies on the minus strand of chromosome 17, at chr17:7,661,779-7,687,546. Ensembl knows 40 transcripts of it. Its canonical transcript, TP53-201 (ENST00000269305), which is also the MANE Select transcript, has 11 exons and makes the 393 amino acids of the protein ENSP00000269305. To list a gene’s transcripts, tick Only list each gene’s transcripts in the tool, or use --list in the terminal (below).
2 · Make the pictures
Genes, transcripts and proteins as pxil pictures makes, for each transcript you choose, a picture of the gene’s sequence, of the spliced transcript and of the protein. From says whose: the reference, either of the person’s haplotypes, or all three, so you can compare them.
It writes nine files into ~/results/pxil/TP53 – three pictures each from the reference and from HG002’s two haplotypes – and says how each differs from the reference’s:
TP53_ENSG00000141510_HG002-hap1.gene.pxil.png (25,757 bases): 39 changes from the reference
TP53-201_ENST00000269305_HG002-hap1.transcript.pxil.png (2,511 bases): 2 changes from the reference in its exons
TP53-201_ENSP00000269305_HG002-hap1.protein.pxil.png (394 amino acids): differs from the reference: P72R
HG002’s copies of the gene differ from the reference at dozens of places, almost all of them in introns, which are spliced out. Two changes fall in the transcript’s exons. One changes the protein: proline 72 becomes arginine. The other, one T fewer in a run of Ts, is in the 3′ untranslated region, after the stop codon, which is why the transcript is a base shorter. The protein has 394 residues: 393 amino acids and the stop codon, written *.
3 · Look at a picture
Open a protein picture in the Files tab. It is an ordinary PNG, 20 pixels a row for 394 residues, each amino acid in its own colour, and the Files tab shows it large, with what it says about itself – the protein, its transcript and gene, its chromosome, and the note differs from the reference: P72R – and its sequence underneath. Its name says whose it is: _HG002-hap1. Amino acid 72 is in the fourth row, twelfth from the left. Point at it: in HG002’s picture the line under it says amino acid 72: R (arginine), in the reference’s amino acid 72: P (proline), and the two pixels have different colours.
The colours are only for the eye. What matters is the pixel values: each one is a residue’s place in the file’s alphabet, so the sequence can be read back exactly, by any program that reads PNG – the Files tab reads it in the page, without Python. Copy the sequence puts it on the clipboard. pxil pictures of sequences explains the format.
4 · The sequences back, as FASTA
pxil pictures → FASTA reads the sequences back from the pictures. Choose the three protein pictures, and the FASTA file has the reference’s TP53 and HG002’s two, which you can compare in any alignment program.
Tick Their metadata (JSON) instead of their sequences to see what each picture says about itself: its gene, transcript and protein IDs, its exons and coding region, the sample and haplotype it came from, and its notes, such as differs from the reference: P72R.
5 · How the sequences are made
- A haplotype’s sequence is the reference with the person’s variants applied: substitutions replace bases, deletions remove them, insertions are put after their anchor base, and bases whose state isn’t known become N.
- The transcript is the haplotype’s sequence over each exon, joined, and read 5′ to 3′: for a gene on the minus strand, such as TP53, that means reverse-complemented.
- The protein is translated from the annotated start codon to the first stop codon. A frameshift is followed into the 3′ untranslated region, and a protein whose stop codon is lost runs to the end of the transcript (and the file says so).
- Selenocysteines (TGA read as U) are kept where Ensembl annotates them, and genes on the mitochondrion use the vertebrate mitochondrial code.
6 · Other genes, other transcripts
- Genes takes several symbols or Ensembl IDs, separated by spaces. HG002’s example file holds only chromosome 17, so its genes must be there (BRCA1 is, and HG002’s copies of its protein are the same as the reference’s).
- Any gene of GRCh38 can be drawn from the reference itself, from its chromosome in
/data/grch38: CFTR from /data/grch38/chr7.gpxil.tif, say.
- Transcripts: the canonical one (the default), the MANE transcripts, every protein-coding one, or every one.
- Genes from: by default, the Gpxil browser’s gene files, every transcript of Ensembl release 116 on GRCh38, read from the web a gene at a time. For another annotation, or another assembly, give a GTF file, such as
/data/examples/TP53.gtf, TP53’s lines of Ensembl 116’s GTF, which also works without the web.
The same in the terminal
gpxil2pxil /data/examples/HG002_chr17.gpxil.tif TP53 --annotation /data/examples/TP53.gtf --list > ~/results/TP53-transcripts.txt
head -3 ~/results/TP53-transcripts.txt
gpxil2pxil /data/examples/HG002_chr17.gpxil.tif TP53 --haplotypes all --annotation /data/examples/TP53.gtf -o ~/results/pxil/TP53
pxil ~/results/pxil/TP53/*.protein.pxil.png -o ~/results/TP53-proteins.fa
grep -c '>' ~/results/TP53-proteins.fa
pxil --info ~/results/pxil/TP53/TP53-201_ENSP00000269305_HG002-hap1.protein.pxil.png
Without --annotation, gpxil2pxil uses the Gpxil browser’s gene files, as the tool does: gpxil2pxil /data/grch38/chr7.gpxil.tif CFTR --kinds protein -o ~/results/pxil/CFTR.
The same in Python
from gpxiltools import GpxilFile
from gpxil2pxil import GTF, Source, make
gene = GTF("/data/examples/TP53.gtf").find("TP53")
t = gene.canonical()
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
ref = Source(g).protein(t) # the reference's protein, as a string
hap1 = Source(g, 1).protein(t) # HG002's first haplotype's
print(t.name, len(ref), ref[66:76], hap1[66:76])
print([f"{a}{i + 1}{b}" for i, (a, b) in enumerate(zip(ref, hap1)) if a != b])
for f in make(g, gene, kinds=["protein"], haplotypes=(None, 1, 2), out_dir="/home/user/results/pxil/TP53-python"):
print(f.path, f.notes)
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- Ensembl (GRCh38 sequence, and genes from release 116)
- Yates AD, Austine-Orimoloye O, Azov AG, Barba M, Barnes I, Barrera-Enriquez VP, et al. (2026) Ensembl 2026. Nucleic Acids Research 54(D1): D1053–D1060. doi:10.1093/nar/gkaf1239
- MANE transcripts
- Morales J, Pujar S, Loveland JE, Astashyn A, Bennett R, Berry A, et al. (2022) A joint NCBI and EMBL-EBI transcript set for clinical genomics and research. Nature 604(7905): 310–315. doi:10.1038/s41586-022-04558-8
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
Get sequences and variants back out
Other programs read FASTA and VCF, not gpxil. This walkthrough writes a person’s two versions of a region as FASTA, their genotype as a single sequence of IUPAC codes, and their variants as a VCF file – and then turns that VCF back into a gpxil file, to show that nothing was lost on the way.
1 · A person’s two haplotypes
Sequences as FASTA writes the sequence of either haplotype over a region. It is the reference with the person’s variants applied: substitutions replace bases, deletions remove them, insertions are put after their anchor base, and bases that weren’t called become N. So a haplotype’s sequence can be shorter or longer than the region. Lower case, for the reference’s soft-masked repeats, is kept.
TP53 is 25,768 bases in the reference; HG002’s first copy of it is 25,757 bases, his second 25,767.
2 · The genotype as one sequence
the genotype, in IUPAC codes writes one sequence as long as the reference, with each base where the two haplotypes differ as the IUPAC code of both: R for A and G, Y for C and T, and so on. It’s the form many programs that expect one sequence for a person can read. A base deleted from one haplotype shows the other’s base; one not called is N.
For the worked example, whose reference is ACGTACGTac, the two haplotypes are ACGTGTGGnc and ATGTACGTGGgc, and the genotype is AYGTACGTNc: Y where one haplotype has C and the other T.
3 · The variants, as VCF
Variants as VCF writes a VCF record for each place where either haplotype differs from the reference, with the genotype phased (|) where the file says it was, and with the genotype quality, read depth and phase set where the file has them. A record that didn’t pass its filters is marked gpxil_filtered.
For TP53 it writes 21 variants and 9 no-call blocks: records with ALT <*>, an END and genotype ./., for the stretches that weren’t called with confidence. They let a program tell “not called” from “the same as the reference”; untick Write no-call blocks to leave them out. The VCF of a region has exactly the records that the VCF of the whole chromosome has there, so a deletion that starts just before the region is written whole.
The records are equivalent to the ones the file was made from, not copies of them: each says what the two haplotypes carry over a stretch of the reference, and neighbouring changes may be joined into one record. IDs, QUAL, INFO and the other FORMAT fields of the original aren’t kept in a gpxil file, so they aren’t written.
4 · And back again
Encoding the exported VCF again, with the gpxil file itself as the reference, gives the same planes. The example workflow Back to FASTA and VCF, and round again does all of this in one go – both haplotypes, the VCF, the new gpxil file, and a check of it.
The same in the terminal
gpxil fasta /data/examples/HG002_chr17.gpxil.tif --which 1 --region chr17:7661779-7687546 -o ~/results/TP53_hap1.fa
gpxil fasta /data/examples/HG002_chr17.gpxil.tif --which 2 --region chr17:7661779-7687546 -o ~/results/TP53_hap2.fa
gpxil fasta /data/examples/worked-example.gpxil.tif --which iupac
gpxil vcf /data/examples/HG002_chr17.gpxil.tif --region chr17:7661779-7687546 -o ~/results/TP53.vcf
grep -v '^#' ~/results/TP53.vcf | grep -v '<\*>' | head -4
gpxil vcf /data/examples/worked-example.gpxil.tif -o ~/results/worked-example.vcf
gpxil encode /data/examples/worked-example.fa -v ~/results/worked-example.vcf --width 4 -o ~/results/worked-example.again.gpxil.tif
gpxil fasta ~/results/worked-example.again.gpxil.tif --which 2
Without -o, gpxil fasta and gpxil vcf print to the screen, so their output can go straight into another command. --region can be given more than once to gpxil vcf, and --contigs chr1,chr2 writes whole contigs.
The same in Python
from gpxiltools import GpxilFile
from gpxiltools.export import genotype_iupac_sequence, haplotype_sequence, write_vcf
with GpxilFile("/data/examples/worked-example.gpxil.tif") as g:
c = g["chrT"]
print(c.sequence()) # the reference
print(haplotype_sequence(c, 1).decode(), haplotype_sequence(c, 2).decode())
print(genotype_iupac_sequence(c).decode())
with open("/home/user/results/worked-example.vcf", "w") as out:
print(write_vcf(g, out), "records")
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- VCF, the variant call format
- Danecek P, Auton A, Abecasis G, Albers CA, Banks E, DePristo MA, et al. (2011) The variant call format and VCFtools. Bioinformatics 27(15): 2156–2158. doi:10.1093/bioinformatics/btr330
- GA4GH and the SAMtools team (hts-specs). The Variant Call Format specification, VCFv4.4. samtools.github.io/hts-specs/VCFv4.4.pdf
- IUPAC nucleotide codes
- Cornish-Bowden A (1985) Nomenclature for incompletely specified bases in nucleic acid sequences: recommendations 1984. Nucleic Acids Research 13(9): 3021–3030. doi:10.1093/nar/13.9.3021
Print a chromosome as a book
At one dot a base, a page of A4 printed at 300 dots per inch holds 6,314,220 bases, so HG002’s chromosome 17 fills 14 pages and the whole human genome about 500. Print it as a book lays a gpxil file out as a PDF of such pages, ready to print, with each page’s first and last position in its header.
1 · A proof first
A whole chromosome takes a while to lay out, so start with a proof of the first few pages. A book begins with a cover (with a key to the colours), a page on how to read it and a title page for each chromosome; with At most this many pages set to 5, the proof has two pages of bases.
Open the PDF in the Files tab, or download it. Zoom in on a page of bases and you will see each base as a square dot, as in the Viewer.
2 · Choose the look
- Show
- The same four views as a picture: the reference’s bases, the person’s genotype (the default for a file with a sample), their two haplotypes in pairs of rows, or their variants on the faded reference.
- For
- print gives each colour directly in ink (CMYK), in the palette’s own ink values, so that nothing is lost in a conversion from screen colours. the screen gives RGB colours, for a PDF to read on a computer.
- Paper, Dots per inch, Dots per base
- A4 at 300 dots per inch, one dot a base, is the default. At 600 dots per inch a page holds four times as many bases (the whole genome then fills about 160 pages, rather than 530); with 2 dots per base, each base is a 2 × 2 square, easier to see and to scan back.
- Colours
- One of gpxil’s palettes (The palettes). process uses the four printing inks at full strength, which print most faithfully.
3 · The whole chromosome
Clear At most this many pages and run it again. HG002’s chromosome 17 becomes a PDF of 17 pages, about 22 MB. It takes about half a minute on a desktop computer, longer in the browser, and longer still on a phone. A small chromosome is quicker: the mitochondrion’s 16,569 bases fit on one page.
A book of a whole genome, several hundred pages, is better made on your own computer, where it has more memory and time (On your own computer).
A book of a person is their genome
A book printed at one dot a base holds the genome in printed form: scanned at a high enough resolution, it can be read back. A book made from a person’s gpxil file – in the genotype, haplotypes or variants views – is as identifying as the file itself, and says things about their relatives too. Treat it with the same care. A book of the reference alone identifies no one.
4 · Printing it
Print at the PDF’s actual size (“100 %” or “actual size”, not “fit to page”), on the paper you chose, at the printer’s full resolution. The colours are given in device CMYK, so ask the print shop to proof them with their printer’s colour profile. Every base is a square dot with smoothing turned off, so the PDF stays sharp at any zoom.
The same in the terminal
gpxil book /data/examples/HG002_chr17.gpxil.tif -o ~/results/HG002_chr17_proof.pdf --contigs chr17 --max-pages 5
gpxil book /data/grch38/chrM.gpxil.tif -o ~/results/chrM_book.pdf --mode reference --palette process
gpxil book /data/grch38/chrM.gpxil.tif -o ~/results/chrM_book_rgb.pdf --mode reference --rgb --dpi 150 --scale 2 --title "Our mitochondria"
The same in Python
from gpxiltools import GpxilFile
from gpxiltools.book import BookLayout, make_book
layout = BookLayout(page="A4", dpi=300, margin_mm=15, scale=1)
print(f"{layout.bases_per_page():,} bases a page")
with GpxilFile("/data/grch38/chrM.gpxil.tif") as g:
info = make_book(g, "/home/user/results/chrM_from_python.pdf", mode="reference", palette="gpxi", colorspace="cmyk", layout=layout)
print(info)
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
Do the same steps again: workflows
A workflow chains tools together: the files one step writes go straight into the next, so a job of several steps runs in one go, and can run again on another file. This walkthrough runs one of the example workflows, runs it again on a file of your own, changes it, and makes a workflow from tools you’ve already run.
1 · The workflows
Workflows, at the top of the Tools tab, lists your workflows, the examples and the runs so far.
| Example | What it does |
| From a VCF to a gpxil file, checked and drawn | encodes HG002’s variants around TP53 on chromosome 17 of GRCh38, checks the new file, draws TP53 and makes pictures of his TP53 proteins |
| A person’s own TP53: pictures and sequences | TP53’s gene, transcript and protein from both of HG002’s haplotypes and the reference, as pxil pictures, and their sequences as FASTA |
| Back to FASTA and VCF, and round again | TP53’s two haplotypes as FASTA, its variants as VCF, and that VCF encoded again and checked |
| Pictures of a gene, and a proof to print | TP53 drawn three ways, and a proof of chromosome 17 as a book |
2 · Run one
Open A person’s own TP53. The run page lists the workflow’s inputs, each with a file already chosen, and the folder for the results. Press Run it.
The steps run one after another, each with the files the steps before it wrote. As each finishes, it says what it did; open it to see its command and what it printed. A step whose files a failed step should have written doesn’t run, and says so.
3 · What a run leaves
Each run has a folder of its own, ~/results/workflow/input, here ~/results/a-person-s-own-tp53-pictures-and-sequences/hg002-chr17. It holds the files the steps wrote, and two more:
workflow-run.json, the record of the run: the files given, each step’s command, what it printed and what it wrote. Open it to see the run again, step by step.
commands.sh, the steps as terminal commands. bash commands.sh in the terminal runs them again, and on your own computer they run as they are, once the packages are installed.
Open the steps as a notebook, under a finished run, turns its steps into a notebook of Python, a cell for each step, to change and run a piece at a time.
4 · Run it on another file
Run the same workflow on any gpxil file with a sample: choose it in the first input. If you made ~/genomes/HG002_TP53.gpxil.tif in Turn someone’s VCF into a gpxil file, try it on that.
The settings that come from a file – the region of a picture, say – are worked out for each file, as the Tools tab works them out, unless the workflow gives them.
5 · Change it
An example can’t be changed, but you can edit a copy, which is saved in ~/workflows. The editor draws each step as a box: its input files on the left, the files it writes on the right.
- Click a box to see its settings beside the boxes, and change them: to make pictures of BRCA1 as well, put
TP53 BRCA1 in the Genes of the gpxil2pxil step.
- Drag from a file a step writes (the dot on the right of its box) to an input of another step to connect them. Only files of a kind the input takes will connect.
- The + by a file offers the tools that read it, and adds the one you choose, connected.
- + Input file adds an input to the workflow, and + Add a step a tool. Tidy lays the boxes out in columns. The editor saves as you go; the download button saves the workflow file, to keep or to send to someone.
A step with a problem – an input not connected, say, or an input of the workflow that nothing reads – is marked on its box. Click an empty part of the editor to see the workflow’s own settings, its name and what it does, with its problems listed under Checks. It won’t run until they’re fixed.
6 · A workflow from what you’ve done
If you’ve just run a few tools by hand, one after another, Make one from the tools you’ve run turns them into a workflow: the files you started from become its inputs, and a file that one tool wrote and the next read becomes a connection. The settings you changed are kept.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
Work with a gpxil file pixel by pixel, in Python
In Python, each plane of a gpxil file is a NumPy array with one number for each base, so the questions you ask of a genome – where does this person differ from the reference, and how; what wasn’t called; how do their two copies differ – are a line or two of array arithmetic. This walkthrough asks them of HG002’s TP53, and of his whole chromosome 17, and ends by writing a gpxil file with a plane of its own.
Each box below is a cell to run in the Notebook tab: press Add to notebook on it, or open the starting notebook, which goes through the same ground. Every box starts by opening the file, so you can run any of them on its own.
1 · Open a file
GpxilFile opens a gpxil file: a path, or the address of one on the web. It reads only the metadata; each contig is an object that knows its name, its length, its layout width and its planes.
from gpxiltools import GpxilFile
g = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")
print(g.sample_id, g.contigs)
c = g["chr17"]
print(f"{c.length:,} bases, {c.width} a row, {c.rows:,} rows; planes {c.planes}")
2 · Read a region of each plane
c.read(plane, start, end) gives a plane’s values over a region as a NumPy array. Positions in Python count from 0 and the end isn’t included, so TP53, chr17:7,661,779-7,687,546 as a genome browser writes it, is start=7_661_778, end=7_687_546. Only the strips of the file that hold the region are read and decompressed: a few hundred kilobytes for a gene, however big the file.
from gpxiltools import GpxilFile
from gpxiltools import codes as C
c = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")["chr17"]
start, end = 7_661_778, 7_687_546 # TP53
ref = c.read("ref", start, end)
gt = c.read("gt", start, end)
flags = c.read("flags", start, end)
print(ref.dtype, ref.shape, gt.dtype, flags.dtype)
print(ref[:12]) # codes …
print(C.ref_to_ascii(ref[:60]).decode()) # … as letters (lower case: soft-masked)
print(c.sequence(start, start + 60)) # the same, in one step
3 · Decode the codes
Every value is a code (The codes). A ref value is a base as a set of bits – A 1, C 2, G 4, T 8 – plus 16 for a soft-masked base. A gt value holds two states, one in each half of the byte: the high four bits for haplotype 1, the low four for haplotype 2. A state is 0 for the reference’s base, or the bits of another base, or one of the special states: 10 ABSENT, 12 COMPLEX, 13 DEL (deleted), 15 UNKNOWN. HG002’s variant at chr17:7,676,154, which changes TP53’s amino acid 72:
from gpxiltools import GpxilFile
from gpxiltools import codes as C
c = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")["chr17"]
start, end = 7_661_778, 7_687_546
ref, gt, flags = (c.read(p, start, end) for p in ("ref", "gt", "flags"))
i = 7_676_154 - 1 - start # chr17:7,676,154 in the region
print("reference", C.ref_to_ascii(ref[i:i + 1]).decode())
print("gt", hex(gt[i]), "=", C.describe_gt_byte(int(gt[i])))
print("haplotype 1:", C.hap1(gt)[i], " haplotype 2:", C.hap2(gt)[i])
print("flags", flags[i], [name for bit, name in C.FLAG_NAMES.items() if flags[i] & bit])
The reference has G; the gt value 0x22 is C on both haplotypes (2 is C); the flag is VARIANT_SITE, “a call covers this base”, without PHASED. The quality planes say how sure the call is: c.read("gq", …) and c.read("dp", …) are 254 here, their highest value (255 means not known).
4 · Find the variants
Whole arrays at once: split gt into its two haplotypes, and a base is heterozygous where one haplotype carries another base and the other the reference’s, homozygous where both carry the same other base.
import numpy as np
from gpxiltools import GpxilFile
from gpxiltools import codes as C
c = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")["chr17"]
start, end = 7_661_778, 7_687_546
gt, flags = c.read("gt", start, end), c.read("flags", start, end)
h1, h2 = C.hap1(gt), C.hap2(gt)
sub1, sub2 = np.isin(h1, C.HAP_SUB_CODES), np.isin(h2, C.HAP_SUB_CODES)
het = (sub1 & (h2 == C.HAP_REF)) | (sub2 & (h1 == C.HAP_REF)) | (sub1 & sub2 & (h1 != h2))
hom = sub1 & sub2 & (h1 == h2)
deleted = (h1 == C.HAP_DEL) | (h2 == C.HAP_DEL)
inserted = (flags & (C.FLAG_INS1 | C.FLAG_INS2)) != 0
unknown = (h1 == C.HAP_UNKNOWN) | (h2 == C.HAP_UNKNOWN)
print("heterozygous", het.sum(), "homozygous", hom.sum(), "deleted bases", deleted.sum(),
"insertions after", inserted.sum(), "not called", unknown.sum())
print("homozygous at", (np.nonzero(hom)[0] + start + 1).tolist()) # back to 1-based positions
In TP53, HG002 has 3 heterozygous and 11 homozygous substitutions, 27 deleted bases and 2 insertions, and 970 bases weren’t called. np.nonzero gives where in the region; adding start + 1 turns those back into positions as a genome browser writes them.
5 · A whole chromosome, a piece at a time
The same counts over the whole of chromosome 17. Reading it in pieces of 8 million bases keeps the memory needed small – 8 MB for each plane read, and a few times that for the arrays worked out from it – however long the chromosome. This is how to go through a whole genome in the browser (A whole genome without running out of memory).
import numpy as np
from gpxiltools import GpxilFile
from gpxiltools import codes as C
c = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")["chr17"]
step = 8_388_608
het = hom = unknown = 0
for a in range(0, c.length, step):
gt = c.read("gt", a, min(a + step, c.length))
h1, h2 = C.hap1(gt), C.hap2(gt)
sub1, sub2 = np.isin(h1, C.HAP_SUB_CODES), np.isin(h2, C.HAP_SUB_CODES)
het += int(((sub1 & (h2 == C.HAP_REF)) | (sub2 & (h1 == C.HAP_REF)) | (sub1 & sub2 & (h1 != h2))).sum())
hom += int((sub1 & sub2 & (h1 == h2)).sum())
unknown += int(((h1 == C.HAP_UNKNOWN) & (h2 == C.HAP_UNKNOWN)).sum())
print(f"{het:,} heterozygous and {hom:,} homozygous substitutions")
print(f"{unknown:,} of {c.length:,} bases not called ({unknown / c.length:.1%})")
HG002 has 54,935 heterozygous and 34,713 homozygous substitutions on chromosome 17, and 13.1 % of it wasn’t called with confidence by the benchmark: mostly centromeres, segmental duplications and other hard places. gpxil stats makes these counts, and more – insertions, deletions, Ti/Tv, phasing, quality – in one go, in the same way (Count what a file holds).
6 · Compare the two haplotypes
Where do HG002’s two copies of TP53 differ from each other? Where their states differ. With phased calls (PHASED in flags), haplotype 1 is one parent’s copy throughout a phase set (the ps plane); HG002’s calls here aren’t phased, so it only says which bases are heterozygous. The sequences themselves come from haplotype_sequence, which applies the variants to the reference, insertions and deletions included.
import numpy as np
from gpxiltools import GpxilFile
from gpxiltools import codes as C
from gpxiltools.export import haplotype_sequence
c = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")["chr17"]
start, end = 7_661_778, 7_687_546
gt = c.read("gt", start, end)
differ = C.hap1(gt) != C.hap2(gt)
print(differ.sum(), "bases where the two haplotypes differ:", (np.nonzero(differ)[0] + start + 1).tolist())
s1 = haplotype_sequence(c, 1, start, end).decode()
s2 = haplotype_sequence(c, 2, start, end).decode()
print(len(s1), len(s2), "bases, against", end - start, "in the reference")
7 · A picture of your own
Any array of colours is a picture. Here each base of TP53 is coloured by what HG002 has there, laid out 256 bases a row, and enlarged three times. Pillow saves it, and a notebook shows it when it is the last line of a cell.
import numpy as np
from PIL import Image
from gpxiltools import GpxilFile
from gpxiltools import codes as C
c = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")["chr17"]
start, end = 7_661_778, 7_687_546
gt = c.read("gt", start, end)
h1, h2 = C.hap1(gt), C.hap2(gt)
sub1, sub2 = np.isin(h1, C.HAP_SUB_CODES), np.isin(h2, C.HAP_SUB_CODES)
colour = np.full((gt.size, 3), 235, np.uint8) # the reference: light grey
colour[(h1 == C.HAP_UNKNOWN) | (h2 == C.HAP_UNKNOWN)] = (170, 170, 190) # not called
colour[sub1 ^ sub2] = (255, 140, 0) # one haplotype: orange
colour[sub1 & sub2] = (200, 0, 0) # both: red
colour[(h1 == C.HAP_DEL) | (h2 == C.HAP_DEL)] = (20, 20, 20) # deleted: black
width = 256
rows = -(-gt.size // width) # rounded up
pixels = np.full((rows * width, 3), 255, np.uint8)
pixels[:gt.size] = colour
picture = Image.fromarray(pixels.reshape(rows, width, 3)).resize((width * 3, rows * 3), Image.NEAREST)
picture.save("/home/user/results/TP53_my_picture.png")
picture
gpxiltools.render does the same with the palettes, views and print colours of gpxil render (Draw a gene, a region or a chromosome).
8 · Write FASTA and VCF of a region
from gpxiltools import GpxilFile
from gpxiltools.export import iter_haplotype, write_fasta_stream, write_vcf
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
c = g["chr17"]
with open("/home/user/results/TP53_haplotypes.fa", "wb") as out:
for h in (1, 2):
write_fasta_stream(out, f"TP53_HG002_hap{h}", iter_haplotype(c, h, 7_661_778, 7_687_546))
with open("/home/user/results/TP53_from_python.vcf", "w") as out:
n = write_vcf(g, out, regions=[("chr17", 7_661_778, 7_687_546)], nocall_blocks=False)
print(n, "VCF records")
9 · A gpxil file of your own, with a plane of your own
GpxilWriter writes gpxil files: give it each contig’s planes as arrays. Besides the standard planes, a file can carry planes of your own, whose names start with x-. Here the mitochondrion’s reference is copied from GRCh38, with a new plane, x-gc: the percentage of G and C in the 101 bases around each base. The checksums are copied too, so the new file can prove that its reference is GRCh38’s.
import numpy as np
from gpxiltools import GpxilFile, GpxilWriter
from gpxiltools import codes as C
from gpxiltools.validate import validate
with GpxilFile("/data/grch38/chrM.gpxil.tif") as g:
c = g["chrM"]
ref = c.read("ref")
checksums = {k: c.entry[k] for k in ("md5", "refget")}
reference = g.metadata["reference"]
base = C.ref_base_nibble(ref) # without the soft-mask bit
gc = np.isin(base, (C.BASE_G, C.BASE_C)).astype(float)
gc_101 = np.round(np.convolve(gc, np.ones(101) / 101, mode="same") * 100).astype(np.uint8)
with GpxilWriter("/home/user/genomes/chrM_gc.gpxil.tif", width=128, reference=reference) as w:
w.add_contig("chrM", {"ref": ref, "x-gc": gc_101}, **checksums)
print(validate("/home/user/genomes/chrM_gc.gpxil.tif").ok, GpxilFile("/home/user/genomes/chrM_gc.gpxil.tif")["chrM"].planes)
A reader that doesn’t know a plane of your own ignores it, so the file is still a gpxil file to every tool, and gpxil validate passes it. A plane with a sample’s genotype needs GpxilWriter(…, sample={"id": …}) and a flags plane beside its gt; gpxil encode makes those from a VCF.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- NumPy
- Harris CR, Millman KJ, van der Walt SJ, Gommers R, Virtanen P, Cournapeau D, et al. (2020) Array programming with NumPy. Nature 585(7825): 357–362. doi:10.1038/s41586-020-2649-2
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
A whole genome without running out of memory
A human genome is 3.1 billion bases. As one byte a base, a single plane of it is 3.1 GB in memory, more than Python in a browser can use, although the file that holds it is under 1 GB. Yet the studio opens a whole genome at once, because it never reads more of a file than it needs. This walkthrough shows how, and how to write your own code the same way.
1 · Read regions, not files
Each plane of a gpxil file is stored in strips of whole rows: 32 rows of 8,192 bases, 262,144 bases a strip, in a file made with the default layout. Reading a region decompresses only the strips that hold it. A gene is one strip or two, so reading it costs the same in a file of one chromosome as in a file of the whole genome.
The studio adds one more step. A file of 8 MB or more is never copied into Python’s memory: Python reads it where the browser keeps it, or from the web, a piece at a time. The chromosomes of GRCh38 in /data/grch38 are read from the Gpxil browser’s site like this, a megabyte at a time, as they’re needed. And the Viewer and the Files tab read gpxil files without Python at all.
2 · A chromosome, a piece at a time
To go through a whole chromosome, read it in pieces and keep only what you work out from each. This finds the GC content of each megabase of chromosome 21, read from the web: at any moment, Python holds one megabase.
import numpy as np
from gpxiltools import GpxilFile
from gpxiltools import codes as C
c = GpxilFile("/data/grch38/chr21.gpxil.tif")["chr21"]
step = 1_000_000
gc = []
for a in range(0, c.length, step):
base = C.ref_base_nibble(c.read("ref", a, min(a + step, c.length)))
acgt = np.isin(base, (C.BASE_A, C.BASE_C, C.BASE_G, C.BASE_T)).sum()
gc.append(np.isin(base, (C.BASE_G, C.BASE_C)).sum() / acgt if acgt else np.nan)
gc = np.array(gc)
print(f"{c.length:,} bases in {gc.size} pieces, {np.isnan(gc).sum()} of them all N")
print(f"GC from {np.nanmin(gc):.1%} to {np.nanmax(gc):.1%}, highest in megabase {np.nanargmax(gc) + 1}")
Chromosome 21’s first five megabases are all N – the short arm, which GRCh38 doesn’t spell out – and its GC content runs from 34.6 % to 53.0 %, highest near the end of its long arm. A loop over all the chromosomes of a genome works the same way, contig by contig: for c in g:. gpxil stats goes through a file like this to count what it holds (section 4).
3 · Files without the reference, which borrow it
A person’s genome as a gpxil file is mostly their reference: the same bases for everyone. Written without the reference plane (gpxil encode --no-ref), it is a small fraction of the size, and names its reference by checksums. Open it with the reference, and gpxiltools reads just the region you ask for from the reference, after checking that its checksums match. The Viewer does the same by itself for a file made on GRCh38.
from gpxiltools import GpxilFile
from gpxiltools.encode import encode
from gpxiltools.export import haplotype_sequence
encode("/data/grch38/chr17.gpxil.tif", "/home/user/genomes/HG002_TP53.noref.gpxil.tif",
vcf="/data/examples/HG002_TP53.vcf", callable_bed="/data/examples/HG002_TP53.bed",
planes=["gt", "flags"], include_ref=False)
with GpxilFile("/home/user/genomes/HG002_TP53.noref.gpxil.tif", reference="/data/grch38/chr17.gpxil.tif") as g:
c = g["chr17"]
print(c.planes, "plus the borrowed reference:", c.has("ref"))
print(haplotype_sequence(c, 1, 7_676_100, 7_676_200).decode())
4 · A working copy, and what it holds
To work on part of a big file, make a working copy of it: gpxil subset copies the contigs you choose, and the planes you choose, into a new file. It copies the pages’ strips as they are stored, without decoding them, so it takes little memory and little time, even from a whole genome or a file on the web. Leaving out the reference plane makes the copy about a hundred times smaller: HG002’s chromosome 17 goes from 24.6 MB to 1.9 MB, and the Viewer borrows the reference from GRCh38 as before.
gpxil subset /data/examples/HG002_chr17.gpxil.tif chr17 --no-ref -o ~/genomes/HG002_chr17.noref.gpxil.tif
gpxil stats ~/genomes/HG002_chr17.noref.gpxil.tif
gpxil stats then counts what the copy holds, a piece at a time: 86.9 % of chromosome 17 called, 54,935 heterozygous and 34,713 homozygous substitutions, and the insertions, deletions, phasing, quality and depth (Count what a file holds). Without the reference plane it counts all but the reference’s bases and Ti/Tv; give it --reference /data/grch38/chr17.gpxil.tif for those. The Viewer counts the same for the region in view, under its key.
5 · What uses memory, and what doesn’t
- Doesn’t: opening a file, its metadata, the Viewer, the Files tab, reading a region, a loop over pieces,
gpxil info, gpxil vcf and gpxil fasta (which write as they go), gpxil validate, gpxil stats and gpxil compare (which read a piece at a time), gpxil subset (which copies a strip at a time).
- Does:
gpxil encode, which holds each contig’s planes whole while it works: about 6 bytes a base with the planes gt and flags, so half a gigabyte for chromosome 17 and about 1.5 GB for chromosome 1, more than a browser tab may have. In the browser, encode one chromosome at a time (Only these contigs, or --contigs), and the biggest chromosomes, or a whole genome, on your own computer (section 6). Also a whole plane read at once (c.read("gt") on chromosome 1 is 249 MB), and big pictures, which is why the tools stop at 25 million bases a picture in the browser.
- Keep arrays small:
uint8 as they come, not converted to 64-bit numbers. del big arrays you’ve finished with, and if Python gets slow or stops with a MemoryError, restart it (the Notebook’s Restart, or the Python pill in the Tools tab): your files are safe, only Python’s variables are lost.
6 · The whole genome on your own computer
On your own computer, memory is less tight and the files can be local. The Gpxil browser offers the whole of GRCh38 as one gpxil file, 848.5 MB, which every command here can use as a reference or read like any other:
curl -LO https://github.com/comparativechrono/Gpxil-browser/releases/download/grch38-ensembl116/GRCh38.gpxil.tif
curl -LO https://github.com/comparativechrono/Gpxil-browser/releases/download/grch38-ensembl116/GRCh38.gpxil.tif.sha256
sha256sum -c GRCh38.gpxil.tif.sha256 # on a Mac: shasum -a 256 -c GRCh38.gpxil.tif.sha256
gpxil info GRCh38.gpxil.tif
gpxil encode GRCh38.gpxil.tif -v my.vcf.gz --callable my.bed --no-ref -o my.gpxil.tif
gpxil stats my.gpxil.tif --reference GRCh38.gpxil.tif
gpxil subset my.gpxil.tif chr17 -o my_chr17.gpxil.tif # a working copy to add to the studio
(curl -C - -LO … carries on with a download that was interrupted.) On your own computer says how to install the programs there.
The limits in the browser
| What | At most | Why |
| a file copied into Python | under 8 MB; bigger ones are read a piece at a time | Python’s memory |
| Make a gpxil file (encode) | one chromosome at a time; about 6 bytes a base, so 0.5 GB for chromosome 17 | a contig’s planes are held whole while they’re made |
| the Viewer | 4 million bases drawn at once | the screen; zoom and move for more |
| Draw a region as a picture | 25 million bases and 64 million pixels | a picture is held in memory whole |
| Sequences as FASTA | 50 million bases in one go | the file is text, a byte a base |
| a file kept in the browser | 1 GB each; a warning above 2 GB in all | what browsers let a site keep |
On your own computer, the same commands have no such limits.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- The Gpxil browser, and its GRCh38 files and gene files
- Chronomic Medicine Group, University of Cambridge (2026) The Gpxil browser: the GRCh38 genome as gpxil pictures, in a web browser. pixelverse.cam
- The GRCh38 reference genome
- Schneider VA, Graves-Lindsay T, Howe K, Bouk N, Chen HC, Kitts PA, et al. (2017) Evaluation of GRCh38 and de novo haploid genome assemblies demonstrates the enduring quality of the reference assembly. Genome Research 27(5): 849–864. doi:10.1101/gr.213611.116
A family: comparing genomes
Because every gpxil file on a reference is laid out base for base on it, two genomes can be compared as two pictures, pixel by pixel. This walkthrough compares two benchmarks of one man’s genome, and then the man with his mother and father: which of his variants came from whom, and which neither parent has.
1 · The family
HG002 is a man of the Personal Genome Project, and HG003 and HG004 are his father and mother. The Genome in a Bottle Consortium has benchmarked the genomes of all three, and the examples hold chromosome 17 of each: HG002’s with its reference plane (24.6 MB), and his parents’ without it (1.9 MB each), as made by gpxil subset --no-ref. The Viewer borrows the reference for those from GRCh38. Look at TP53 in the three:
A fourth file, HG002_chr17_v5.0q.noref.gpxil.tif, holds HG002’s chromosome 17 from the newer benchmark, version 5.0q, made from a whole assembly of his genome rather than from reads.
2 · Two benchmarks of one genome
First, the two benchmarks of HG002 against each other, the newer taken as the truth. Where the two differ, gpxil compare spells both out as sequences, in case they only write the same thing differently, as in a repeat, where an insertion can be anchored at either end.
gpxil compare /data/examples/HG002_chr17.gpxil.tif /data/examples/HG002_chr17_v5.0q.noref.gpxil.tif
Where both call with confidence, 83.7 % of the chromosome, 113,164 bases have a variant in one or the other, and all but four are the same in both; 413 of them only written differently. Precision and recall are over 99.99 %. The two stretches that differ hold four substitutions that only the older benchmark has. At 72,809,199, version 4.2.1 writes two deletions as two calls that overlap, unphased, the second starting at a base the first deletes: they can’t be on one haplotype, so the gpxil specification puts them on one each, and there the two benchmarks are the same, written differently. (Version 0.1 of the specification put both on one haplotype, as one longer deletion: comparing these two benchmarks is how that was found, and version 0.2 mended it.)
3 · A son and both his parents
Now HG002 against HG003 and HG004. At each base called in all three, his two alleles must be one from his father and one from his mother.
gpxil compare /data/examples/HG002_chr17.gpxil.tif \
--father /data/examples/HG003_chr17.noref.gpxil.tif --mother /data/examples/HG004_chr17.noref.gpxil.tif \
-o ~/results/HG002_trio_TP53.png --draw chr17:7,661,779-7,687,546 --width 256 --scale 2
- Where his variants came from. Of HG002’s 106,558 variant bases called in all three, 28,419 came from his father, 27,062 from his mother and 40,337 from both (he is homozygous there); for 10,716 – where all three are heterozygous, say – it can’t be told.
- What neither parent has. 22 sites are Mendelian errors, every one inside all three benchmarks’ regions, and at every one HG002 has something neither parent has: 21 substitutions (11 of them C to T or G to A, as new mutations mostly are) and a deleted base. These are candidate new mutations; three of them, the first of them two neighbouring bases, lie within 82 bases of each other, at 58,332,214-58,332,296.
- What was written differently. Three stretches, at 71,915,487, 72,212,793 and 79,736,542, are consistent only once spelled out as sequences: there the three write a deletion and an insertion in a repeat in different ways.
Look at one of them in the Viewer, the first that is C to T, at 4,047,934, in HG002 and in each parent:
HG002 is heterozygous there, C and T (the mix of blue and yellow); both parents are C, the reference.
Both comparisons, each with its picture of TP53, are also an example workflow, to run again on files of your own:
4 · Crossovers
Each parent passes on one of their two copies of chromosome 17, but cut and joined where their two copies crossed over in the egg or sperm. Where a parent’s calls are phased – their variants put on one copy or the other – the child’s genotypes show which copy the child received at each site, and a change from one copy to the other along a phase set is a crossover. The picture shows variants from the parent’s second copy lighter, so a crossover is where the colour changes. HG003’s and HG004’s benchmarks aren’t phased, so the comparison says that it can’t see crossovers in them; with phased parents, it lists them, with where each happened.
5 · The numbers, in Python
from gpxiltools import GpxilFile
from gpxiltools.compare import compare_trio
EX = "/data/examples/"
errors = []
with (
GpxilFile(EX + "HG002_chr17.gpxil.tif") as son,
GpxilFile(EX + "HG003_chr17.noref.gpxil.tif") as father,
GpxilFile(EX + "HG004_chr17.noref.gpxil.tif") as mother,
):
t = compare_trio(son, father, mother, errors=errors)["total"]
print(f"from the father {t['from_father']:,}, from the mother {t['from_mother']:,}, from both {t['from_both']:,}")
for e in errors[:5]:
print(f"{e['position']:,}: son {e['child']}, father {e['father']}, mother {e['mother']}", "(new)" if e["new"] else "")
6 · On your own computer
For whole genomes, compare on your own computer: two genomes take a few seconds a chromosome. Make the parents’ files from Genome in a Bottle’s benchmarks as HG002’s were, without the reference plane:
gpxil encode GRCh38.gpxil.tif -v HG003_GRCh38_1_22_v4.2.1_benchmark.vcf.gz \
--callable HG003_GRCh38_1_22_v4.2.1_benchmark_noinconsistent.bed --no-ref -o HG003.noref.gpxil.tif
gpxil compare HG002.gpxil.tif --father HG003.noref.gpxil.tif --mother HG004.noref.gpxil.tif --json > trio.json
Compare two genomes and A child and both parents say exactly what is counted.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
- The GRCh38 reference genome
- Schneider VA, Graves-Lindsay T, Howe K, Bouk N, Chen HC, Kitts PA, et al. (2017) Evaluation of GRCh38 and de novo haploid genome assemblies demonstrates the enduring quality of the reference assembly. Genome Research 27(5): 849–864. doi:10.1101/gr.213611.116
Pure GA-sequences: looking for patterns
Guenter Albrecht-Buehler drew genomes as pictures, a pixel a base, and in them found pure GA-sequences: stretches of more than 50 bases of nothing but A and G, thousands of them, where chance would give none. He saw Alu elements line up before those that begin with a run of A’s, and reported that they have many times fewer point mutations than the genome at large. This walkthrough follows his questions on chromosome 17 with the tools that look for patterns: find the GA-sequences, stack them, trace where they come from, and count a person’s substitutions in them.
1 · Find them
A pure GA-sequence is a run of A and G of 51 bases or more. On the other strand, the same stretch is a run of C and T, so look on both strands:
gpxil runs /data/examples/HG002_chr17.gpxil.tif --of AG --both-strands -o ~/results/HG002_chr17_AG_runs.bed
Chromosome 17 has 1,561: 739 on the + strand and 822 on the − strand, as many on each as Chargaff’s second rule expects of a genome’s two strands, 140,621 bases in all. Half are 71 bases long or less; the longest is 574. Albrecht-Buehler counted 19,139 in the whole human genome of 2008; GRCh38 has 45,681.
2 · Stack them
His picture of them stacks the GA-sequences one a row, lined up at their starts, each read on its own strand with the 400 bases before it, and sorts the rows by the run of A’s each begins with, longest first:
gpxil stack /data/examples/HG002_chr17.gpxil.tif ~/results/HG002_chr17_AG_runs.bed --sort run --labelled -o ~/results/GA-stack.png
Right of the anchor, the poly(A)s run down as a pink wedge (A, in a soft-masked repeat), longest at the top, turning into mixed A and G below. Left of it, in the top few hundred rows, the bases line up into vertical stripes reaching about 280 bases back, with an A-rich stripe halfway along: the body of an Alu element, whose two halves are joined by a run of A’s. Rows that start without a poly(A) show no stripes.
3 · Where they come from
An Alu element ends in a poly(A) tail. Give the genome RepeatMasker’s repeats as a plane of its own, and look at one of the GA-sequences in the Viewer:
gpxil annotate /data/examples/HG002_chr17.gpxil.tif /data/examples/rmsk_chr17.txt.gz --plane x-repeats -o ~/genomes/HG002_chr17.repeats.gpxil.tif
The GA-sequence at chr17:6,793,028-6,793,302 begins in the tail of an AluJb element (red, a SINE) and runs on as a simple repeat of AGAA (violet): an Alu tail that has become a repeat of A and G. Switch Show to the reference’s bases to see it. To count how many begin where an Alu ends, on the same strand, take the GA-sequences and the repeats into Python:
import numpy as np
from gpxiltools.features import read_features
ga = read_features("/home/user/results/HG002_chr17_AG_runs.bed")
alus = read_features("/data/examples/rmsk_chr17.txt.gz", match="^SINE/Alu/")
# an Alu's 3' end on each strand: its end on +, its start on -
ends = {s: np.sort([a.end if s == "+" else a.start for a in alus if a.strand == s]) for s in "+-"}
def alu_tail(f, near=30):
five = f.start if f.strand == "+" else f.end # (where the GA-sequence begins, on its strand)
e = ends[f.strand]
i = np.searchsorted(e, five - near)
return i < len(e) and e[i] <= five + near
tails = sum(alu_tail(f) for f in ga)
print(f"{tails:,} of {len(ga):,} pure GA-sequences ({100 * tails / len(ga):.0f} %) begin within 30 bases of an Alu's end")
704 of the 1,561 do, 45 %: many GA-sequences began as Alu elements’ poly(A) tails, which, with time, take up G’s and become repeats of A and G. Stack features shows chromosome 17’s Alu elements lined up at their ends by age, their tails pure A in the young and mixed in the old.
4 · Do they have fewer point mutations?
Albrecht-Buehler reported 160 times fewer point mutations in GA-sequences than in the genome at large, a figure his correction withdrew for “many”. HG002’s substitutions in them can be counted, and compared with what their sequence leads one to expect, context by context. Read contexts with a purine in the middle, so a GA-sequence is read on its own strand, and count only isolated substitutions, with no other variant within 20 bases: a repeat’s unit changed is often written as several substitutions side by side.
gpxil spectrum /data/examples/HG002_chr17_v5.0q.noref.gpxil.tif ~/results/HG002_chr17_AG_runs.bed \
--reference /data/examples/HG002_chr17.gpxil.tif --centre purine --isolated 20
| v4.2.1 | v5.0q |
| GA-sequence bases called with confidence | 54,051 (38 %) | 117,343 (83 %) |
| isolated substitutions in them, observed / expected | 28 / 36.5 | 92 / 78.4 |
| … the ratio (95 % interval; p) | 0.77 (0.51–1.11; 0.18) | 1.17 (0.95–1.44; 0.15) |
| all substitutions, observed / expected | 50 / 42.0 | 410 / 96.0 |
| isolated transitions / transversions (the background’s) | 8.3 (2.4) | 4.8 (2.4) |
| insertions and deletions a kilobase (the background’s) | 4.3 (0.21) | 8.4 (0.36) |
Not many times fewer: in both benchmarks, the isolated substitutions are about what their contexts predict, and each ratio’s interval includes 1. The older benchmark leaves most GA-sequences out of the regions it calls with confidence; the newer, made from a whole assembly of his genome, keeps 83 % of their bases, and in them finds four times the substitutions their contexts predict when every one is counted – most of them clustered, changes of a repeat’s units. What GA-sequences do have is 20 to 23 times the insertions and deletions of the rest of the chromosome, as repeats do, and substitutions that lean to transitions, A to G and G to A, which keep a GA-sequence pure.
5 · One up close
Wrap the GA-sequence of step 3 at its period, one unit a row, the reference beside HG002’s two haplotypes:
Its period is 4. Haplotype 1 is the reference but for three more units; haplotype 2 has 15 substituted bases in 13 units, and each of those units becomes one the repeat already has, AAAG becoming AGAG, AGAA becoming AGAG, as if copied from a neighbour – the way repeats change, by slippage and by copying their own units, rather than the way a protected sequence would.
6 · The whole of it, as a workflow
Finding them, stacking them, counting their substitutions (in the v4.2.1 benchmark) and adding the repeats are an example workflow, to run again on a genome of your own:
7 · The whole genome, on your own computer
With the Gpxil browser’s GRCh38 file and a person’s genome encoded on it, the same commands cover every chromosome:
gpxil runs GRCh38.gpxil.tif --of AG --both-strands -o GA.bed
awk '$1 == "chr1"' GA.bed > GA.chr1.bed
gpxil stack GRCh38.gpxil.tif GA.chr1.bed --sort run --labelled -o GA.chr1.png
gpxil spectrum HG002.v5.0q.noref.gpxil.tif GA.bed --reference GRCh38.gpxil.tif --centre purine --isolated 20
Genome-wide, 37 % of GRCh38’s GA-sequences on the autosomes begin at an Alu’s end, 71 % of those that begin with a poly(A) of 15 or more; the older the Alu, the more often its tail is a GA-sequence; HG002’s isolated substitutions in them run at 0.90 and 1.11 times what their contexts predict in the two benchmarks, and the newest variants of 76,215 people in gnomAD at 1.00 times. Experiment 04 of the gpxil scratchpad has it all, with the scripts.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- Pure GA-sequences, and their flanks
- The correction replaces the abstract’s “160 times fewer” point mutations with “many”.
- Albrecht-Buehler G (2008) Properties and distribution of pure GA-sequences of mammalian genomes. PLoS ONE 3(11): e3818. doi:10.1371/journal.pone.0003818
- Albrecht-Buehler G (2009) Correction: Properties and distribution of pure GA-sequences of mammalian genomes. PLoS ONE. doi:10.1371/annotation/7126ab55-ca2a-4202-9e66-fac899bd52ee
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- RepeatMasker, and the UCSC Genome Browser’s table of its repeats on GRCh38 (rmsk_chr17.txt.gz)
- Smit AFA, Hubley R, Green P (2013–2015) RepeatMasker Open-4.0. www.repeatmasker.org
- Kent WJ, Sugnet CW, Furey TS, Roskin KM, Pringle TH, Zahler AM, et al. (2002) The human genome browser at UCSC. Genome Research 12(6): 996–1006. doi:10.1101/gr.229102
- UCSC Genome Browser. RepeatMasker’s repeats on hg38 (GRCh38): the rmsk table. hgdownload.soe.ucsc.edu/goldenPath/hg38/database
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
- Chargaff’s second parity rule
- Rudner R, Karkas JD, Chargaff E (1968) Separation of B. subtilis DNA into complementary strands. 3. Direct analysis. Proceedings of the National Academy of Sciences of the USA 60(3): 921–922. doi:10.1073/pnas.60.3.921
The Viewer
The Viewer shows a gpxil file as it is: one pixel, or a square of pixels, for each base, in rows that read from left to right and from top to bottom like a page of text. It reads only the part of the file it shows, straight from the file, without Python, so a whole genome opens at once – even one on the web.
Choosing what to see
- File
- Any gpxil file: yours, the examples, or a chromosome of the GRCh38 reference, read from the Gpxil browser. Add a file… puts one from your computer into
~/genomes and opens it; it stays in this browser.
- Region
- Choose a contig, or type a region and press Go (or Enter):
chr17:7,661,779-7,687,546, counting from 1 and including both ends, with or without the commas. A contig’s name alone means all of it, and one position, such as chr17:7676154, that base. Positions are always the chromosome’s, in a file of windows too (below).
- Show
- What to draw: the reference’s bases, the person’s genotype, their two haplotypes, their variants, or the flags, genotype quality, read depth or phase sets. Only the views the file can show are offered: a file without a sample has only the reference. The views and their colours
- Colours
- One of gpxil’s palettes: classic, Okabe–Ito (safe for colour-blind readers), the GPxI greys, or the process inks (The palettes).
A file opens at TP53 if it holds it (in chromosome 17, or a window of it), and otherwise at the middle 100,000 bases of its first contig. The Viewer remembers the file, region, view and colours you had last time.
Moving around
- The arrow buttons move left and right by half the view; the magnifier zooms in to half as many bases, and − zooms out to twice as many.
- Double-click a base to zoom in around it.
- Click the picture, and then the keys ← and → move by a quarter of the view, + and − zoom.
The picture is as wide as the tab: as many bases a row as fit, at 1 to 24 pixels a base, so a small region shows big squares and a large one single pixels. The line above the picture gives the region, the number of bases, how many a row and how many pixels each. The Viewer draws at most 4 million bases at once; a bigger region shows its first 4 million.
Pointing at a base
Point at a pixel, and the line under the picture says what the file holds there: the position, the reference’s base (and whether it is soft-masked), each haplotype’s state and the gt value they make, the flags, the genotype quality, the read depth and the phase set. At HG002’s chr17:7,676,154, for example: reference G, haplotype 1 C, haplotype 2 C (gt 34), flags VARIANT_SITE, GQ 254, DP 254. The codes explains every value.
Counting what is in view
Under the key, the Viewer counts what the region in view holds, as gpxil stats does, and follows you as you move: how much of it was called, the person’s heterozygous and homozygous substitutions and their Ti/Tv, insertions and deletions, the share of heterozygous substitutions phased, and the GC content of the reference. All the counts adds the reference’s bases, the phase sets, genotype quality and read depth, and the gpxil stats command that gives the same numbers in the terminal. In HG002’s TP53: 96.2 % called, 3 heterozygous and 11 homozygous substitutions, 2 insertions and 6 deletions (Count what a file holds).
Keeping what you see
Under the picture is the gpxil render command that draws the same picture, pixel for pixel, as a file, ready to run in the terminal; Save the picture saves the view as it is, as a PNG in ~/results. Save this region as a gpxil file keeps the bases in view, with every plane the file has there, as a gpxil file of their own in ~/genomes: a window (below), cut out by gpxil subset. HG002’s TP53 alone is 17 kB; View it opens it. For the flags, quality, depth and phase-set views, which gpxil render doesn’t draw, there’s a Python cell instead, which reads the plane’s values for the region as a NumPy array: Add to notebook puts it in the open notebook.
Windows: part of a chromosome
A gpxil file can hold windows (version 0.3 of the specification): contigs that each hold a stretch of a chromosome, such as /data/examples/HG002_TP53.gpxil.tif – TP53 and 100,000 bases either side, 76 kB cut out of HG002’s chromosome 17. A window keeps the chromosome’s positions, and the Viewer shows it at its place:
- the list of contigs names the stretch each window holds, such as chr17 7,561,779–7,787,546 (a window);
- a region you type is the chromosome’s, so
chr17:7,661,779-7,687,546 is found in the window that holds it, and a region outside the file’s windows gets a note saying which stretches it holds;
- pointing at a base gives its position on the chromosome, and the line above the picture says which window the bases come from;
- the commands under the picture, and the counts, name the region as the chromosome’s, as gpxil reads one.
A window without its reference plane, made on GRCh38, borrows its bases from /data/grch38, as a whole chromosome does. Make windows with Save this region as a gpxil file, with Make a working copy (subset), or straight from a VCF with Make a gpxil file (encode).
Files on the web, and files without a reference
A file on the web, such as GRCh38’s chromosomes in /data/grch38, is read a piece at a time as you move around: the first view of a chromosome takes a moment, and parts you’ve seen are kept for a while. A file written without its reference plane (--no-ref) has only the person’s genotype; if its metadata says its reference is GRCh38, the Viewer reads the bases from /data/grch38 and says so above the picture.
From the terminal
viewer FILE [REGION] opens a file in the Viewer from the terminal, with --plane=gt (or ref, flags, gq, dp, ps) for a view:
viewer /data/examples/HG002_chr17.gpxil.tif chr17:7,661,779-7,687,546
viewer /data/examples/HG002_TP53.gpxil.tif chr17:7,676,001-7,677,000 --plane=gt
viewer /data/grch38/chr21.gpxil.tif chr21:30,000,001-30,500,000 --plane=ref
The views and their colours
Each view of the Viewer draws one or two planes of the file. The key under the picture names its colours; this page says what each one means. The first four views are exactly what gpxil render draws, pixel for pixel, in every palette, so a picture you like in the Viewer can be made as a file with the command under it.
Reference bases
The ref plane: each base in the palette’s colour (in the classic palette, A red, C blue, G green, T yellow). Bases in soft-masked repeats – lower case in the reference – are 45 % paler, except in the GPxI greys, which need all their levels. An ambiguous base is the mix of its bases’ colours. N, a base that isn’t known, has a colour that no base and no mix of bases has – near-black in the classic and Okabe–Ito colours, light blue in the GPxI greys, grey in the process inks – and is never paler. A gap, where there is no base, is the colour of paper.
Genotype
Each base as the person has it, both haplotypes at once, as one IUPAC code (The codes). Where the haplotypes agree, the base’s colour; where they differ, the mix of their two colours, so a heterozygous A/G base has the colour of R, half red and half green. A haplotype that wasn’t called, or that has a complex variant, counts as N, so its base is N’s colour: near-black in the classic colours. A base deleted from one haplotype shows the other haplotype’s base – so look for deletions in the next two views – and a base deleted from both is paper. Soft-masked bases stay paler, as in the reference.
The key lists the colours in view, with each heterozygous pair that is there – A and G, C and T and so on – and how many bases have it. In TP53, HG002 is heterozygous at only three bases, all of them A and G; in the 150,000 bases around it, every pair turns up. Some mixes are greys – in the classic colours, C and T mix to mid-grey – which is why N is near-black: a heterozygous base never looks like one that isn’t known. To see every heterozygous base in one colour, use the Variants view, where they are all orange.
Haplotypes
The two haplotypes separately, as two rows of pixels for each row of bases: haplotype 1 above, haplotype 2 below. Each pixel is that haplotype’s own base: a substitution in its new base’s colour, a deleted base paper (as is haplotype 2 where the person has only one copy), and a base that wasn’t called, or a complex variant, N’s colour (near-black in the classic colours). A heterozygous variant shows in one row and not the other; a homozygous one in both.
Variants
The quickest way to find variants: the reference is faded 80 % towards white, and the bases where the person differs from it are coloured by what kind of change it is.
| Colour | Meaning |
| orange | heterozygous: a substitution on one haplotype and the reference on the other, or a different substitution on each |
| red | homozygous: the same substitution on both haplotypes, or on the only copy where the person has one |
| near black | deleted, on either haplotype |
| green | an insertion follows this base, on either haplotype |
| grey | not called, on either haplotype |
| purple | a complex variant, which can’t be written base by base, such as an inversion |
| faded | the same as the reference |
A base can be more than one of these: then purple wins, then green, then near black, and grey shows only where nothing else does.
Flags
The flags plane, one colour for the most telling flag of each base:
| Colour | Meaning |
| red | FILTERED: a call over this base didn’t pass its filters |
| green | INS1 or INS2: an insertion follows this base |
| blue | VARIANT_SITE and PHASED: a phased call covers this base |
| grey-blue | VARIANT_SITE: a call covers this base (any genotype, the reference’s too) |
| pale blue | PHASED alone |
| white | no flags: the base is as it is by default, not because a call said so |
These are the colours the file’s own colour map gives its flags pages, so an ordinary image viewer shows them the same way.
Genotype quality, read depth
The gq and dp planes, from white for 0 to deep blue for 99 or more (genotype quality) or 60 or more (read depth). Pink means the value isn’t known (255): a base that no call with that field covers.
Phase sets
The ps plane: each phase set in a colour of its own, and white where a base is in none. Within one phase set, haplotype 1 is the same parental copy throughout; between phase sets, it may not be.
Annotation planes
A file can hold planes of its own, named x-…, such as the repeats or genes Add an annotation plane puts in it. Each is a view of the Show list, Annotation: x-repeats (labels) say, and they are drawn by what their bases hold:
| The plane holds | Drawn as |
| labels, such as a repeat’s family | a colour for each label. A repeat class keeps one hue – SINE red, LINE blue, LTR teal, DNA transposons yellow, simple repeats violet, low-complexity sequence pink, satellites green, others another hue – and its families and names are lighter and darker shades of it. The key lists the labels in view, commonest first, with their bases. |
| where features are | blue where there is one |
| scores or values | from light to dark blue, over the lowest and highest values in view |
| nothing at a base (0, or no value) | paper |
Point at a base to read its label or value, with the other planes’ values. The viewer command opens one with --plane=x-repeats.
The same in a file
For the reference, genotype, haplotypes and variants views, the command under the picture is the gpxil render that makes the same picture as a file, with the same bases per row, pixels per base and palette (Draw a region as a picture). gpxil render can also mark the bases that insertions follow in the haplotypes view (--mark-insertions), lay a region out along a Hilbert curve, and write CMYK for print. The flags, quality, depth, phase-set and annotation views are the Viewer’s own: under them, a Python cell reads the plane’s values as a NumPy array (and an annotation plane’s labels), ready to add to the notebook. The codes describes the values behind every colour.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- The Okabe–Ito colours
- Okabe M, Ito K (2002) Color Universal Design (CUD): how to make figures and presentations that are friendly to colorblind people. jfly.uni-koeln.de/color
- Genome pixel images (GPxI)
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
The Tools tab
The Tools tab has a form for each job you can do with gpxil files. Each runs a command of gpxiltools, gpxil2pxil or pxil in the studio’s Python, exactly as the terminal would, and shows you that command, and the same job in Python, so you can do it again without the form.
The first page
The first page has the map of the formats and the tools between them: click an arrow to open its tool, or a box to see what you can do with that kind of file. Under the map, Common tasks lists the jobs most people come for, each with its tools in order and a walkthrough. All tools (on a narrow screen) or the list on the left has every tool, by group:
A tool’s form
- Files are chosen from a list: your files first, newest at the top, then the examples, then the chromosomes of GRCh38. From my computer… adds one, and Look at it opens the chosen file where it belongs.
- When you choose a gpxil file, the form reads what it holds – its contigs, its sample, whether it has its reference – and suggests settings to suit it: a region on its contigs, a view for its sample, the reference for a file written without one. Anything you’ve changed yourself is left alone.
- Under Run, the form says what it still needs, if anything: a region, say, or one small enough for the browser.
- More about this tool explains what it does, with a link to its page in the help.
- The settings you used last are offered again when you come back to the tool on the same visit.
The same in the terminal, and in Python
Under every form, The same in the terminal, and in Python shows the command the form makes, and the same job as Python for a notebook, each with Copy and a button to run it there. As you change the form, they change too, so they’re a good way to learn the commands and the Python.
When it has run
The result says Done and how long it took, or what went wrong. What the program printed is shown under it: its progress in plain text, any warning or error in yellow. Then come the files it wrote, each with buttons to look at it, to view a gpxil file in the Viewer, to check it, to read a pxil picture’s sequence, or to download it. Some tools offer the next step, such as Check it after a gpxil file is made.
A tool writes into your home folder, ~/results unless you choose otherwise, and asks before it replaces a file that’s already there.
A file’s page
More… on a file – in the Files tab, or under a result – opens the file’s page in the Tools tab: what is in it, and every tool that reads it.
Python
The pill at the top right says how Python is: not started, starting (with what it is loading), or ready. Python starts the first time you run a tool on each visit, which takes a few seconds; the very first time, it also downloads about 15 MB, which your browser keeps. Click the pill to see the versions of the packages, or to restart Python, which forgets its variables but not your files.
- WorkflowsTools chained together: the editor, inputs and steps, running, the run’s folder, and the workflow file
- What’s in a gpxil filegpxil info: a file’s reference, sample, contigs and planes, or all its metadata as JSON
- Check a gpxil filegpxil validate: a file against the specification, its codes, its sidecar and its checksums
- Count what a file holdsgpxil stats: bases called, substitutions and Ti/Tv, insertions, deletions, phasing, quality and depth, for every contig or for regions
- Make a gpxil file (encode)gpxil encode: a reference, and one person’s VCF, as a gpxil file
- Make a working copy (subset)gpxil subset: some contigs, and some planes, of a gpxil file in a new and smaller file, copied without being decoded; or a region, such as a gene, cut out as a window
- Compare two genomesgpxil compare: two gpxil files of one person, base by base, the second as the truth: the same, written differently or different, with precision, recall and a picture
- A child and both parents (trio)gpxil compare --father --mother: Mendelian errors and candidate new mutations, where the child’s variants came from, and crossovers where the parents are phased
- Find runs of a compositiongpxil runs: the stretches of a genome made only of some bases – pure GA-sequences, runs of one base, AT-rich stretches – on one strand or both, as a BED file
- Stack features, lined upgpxil stack: features – runs, repeats, genes, any BED file – one a row, lined up at their starts, ends or centres on their own strands, sorted, and drawn as bases or variants, with each column’s bases as a table
- Substitutions in regions, by contextgpxil spectrum: does a person have more or fewer substitutions in some regions than their sequence leads one to expect? Observed against expected, context by context, with exact intervals, on the features’ own strands too
- Add an annotation planegpxil annotate: a copy of a gpxil file with an annotation – repeats, genes, a BED or bedGraph file – as a plane of its own, a value at every base, which the Viewer draws and the tools read
- Strand symmetry: skews in windowsgpxil skew: GC content, the GC and AT skews, and how far each strand’s k-mers are from their reverse complements’ counts (Chargaff’s second rule), in windows along a genome, as a table and a map
- Wrap a repeat at its periodgpxil period: a tandem repeat – a microsatellite, a GA-sequence – wrapped at its period, one unit a row, so the units line up and a change in one stands out; the reference beside the person’s two haplotypes, as a picture or text
- Draw a region as a picturegpxil render: a region in four views, as PNG for the screen or TIFF (RGB or CMYK) for print
- Print it as a book (PDF)gpxil book: a PDF to print with one dot per base, in CMYK or RGB
- The palettesgpxil palettes: the colours of A, C, G, T and N in each palette, for the screen and in ink
- Sequences as FASTAgpxil fasta: the reference, either haplotype, or the genotype in IUPAC codes, as FASTA
- Variants as VCFgpxil vcf: the person’s genotypes as a VCF again, for a region or every contig
- Genes, transcripts and proteins as pxil picturesgpxil2pxil: a gene, its transcripts and proteins, from the reference or a person’s haplotypes, as pxil files
- pxil pictures → FASTApxil: the sequences of pxil pictures as FASTA, or their metadata as JSON
Workflows
A workflow is a set of the Tools tab’s tools joined together, so that each reads the files the ones before it wrote. Draw one in the editor, start from an example, or make one from the tools you’ve run; then run it on the same files again, or on new ones. They work like the workflows of Galaxy. The walkthrough takes you through one.
Inputs and steps
A workflow has inputs, the files it is given when it runs, and steps, each a tool. Each input says which kinds of file it takes – a gpxil file, a VCF, a reference (FASTA or gpxil), a BED file, a GTF file, pxil pictures – and can suggest a file, which the run page offers. An input can be optional, for a file a step can do without, such as a VCF’s BED file of callable regions.
Every tool can be a step except The palettes, which reads and writes nothing. What’s in a gpxil file and Check a gpxil file write no files, so nothing can follow them, but they say what they found in the run. Genes, transcripts and proteins as pxil pictures writes a set of files, which goes as a whole into a step that reads several, such as pxil pictures → FASTA.
The editor
- Each step is a box, with its input files on the left and the files it writes on the right. Drag from a file’s dot to an input of another step to connect them; only files of a kind the input takes will connect, and never in a loop.
- The + by a file lists the tools that can read it, and adds the one you choose, connected to it.
- Click a box to see its settings beside the boxes. A step’s settings are its tool’s form, without the files, which come from the connections, and without where to save, which the run decides.
- + Input file and + Add a step add boxes; Tidy arranges them in columns, inputs on the left. Changes are saved as you make them.
- Click an empty part of the editor for the workflow’s own name and description, and its Checks: anything that stops it running, such as an input not connected.
Running
The run page asks for a file for each input, and for the folder for the results, by default ~/results/workflow/first input. Press Run it, and the steps run one after another, in an order in which each runs after the steps it reads from. Each step shows its state as it goes, and when it’s done, what it did; open it for its command, what it printed and the files it wrote. If a step fails, the steps that need its files are skipped, and the rest still run. Stop stops the run after the step that is running.
A step that would write a file with the same name as one an earlier step wrote writes into a folder of its own, so nothing is overwritten.
What a run leaves
- The files the steps wrote, in the run’s folder.
workflow-run.json, the record of the run, which opens as the run’s page, step by step.
commands.sh, the steps as terminal commands, to run again with bash commands.sh, here or on your own computer.
- On the run’s page, Open the steps as a notebook makes a notebook with a cell of Python for each step.
The workflow file
A workflow is a file of its own, ~/workflows/name.workflow.json: JSON that lists the inputs and steps, the settings of each step that differ from the tool’s, and the connections. Download it from the editor to keep it or to send it to someone; Add a workflow file… on the Workflows page adds one you’ve been given. The examples, in /data/examples/workflows, can’t be changed, but Edit a copy makes one of your own.
What’s in a gpxil file
gpxil info tells you what a gpxil file holds: what wrote it and when, the reference it was made on, the sample and where its calls came from, and for each contig its length, its planes, how many VCF records went into it and how many entries its sidecar has. It reads only the file’s first page, so it’s instant even for a whole genome on the web.
The form
- The gpxil file
- Any gpxil file: yours, an example, or a chromosome of GRCh38.
- All of its metadata, as JSON
- Instead of the summary, the whole of the file’s metadata (The gpxil format): every contig’s checksums and pages, the planes’ types, the sample, and the counts the encoder kept as it read the VCF.
What it says
/data/examples/HG002_chr17.gpxil.tif: gpxil 0.2, written by gpxiltools 0.6.0, 2026-10-28T15:02:24+00:00
reference: GRCh38 (Homo_sapiens.GRCh38.dna_sm.primary_assembly.fa.gz)
sample: HG002 from HG002_GRCh38_1_22_v4.2.1_benchmark.vcf.gz, callable regions HG002_GRCh38_1_22_v4.2.1_benchmark_noinconsistent.bed
layout: raster, width 8192; palette classic
contig length planes records sidecar
chr17 83,257,441 ref,gt,flags,gq,dp,ps 106,992 12,368
records is the number of VCF records encoded on the contig; sidecar the number of inserted sequences and complex variants kept beside the pixels (The codes). A contig of the reference alone has only the ref plane. It writes no files.
In the terminal
gpxil info /data/examples/HG002_chr17.gpxil.tif
gpxil info /data/grch38/chr17.gpxil.tif
gpxil info --json /data/examples/worked-example.gpxil.tif
gpxil info also takes the address of a file on a web server that answers range requests, and reads just its first page.
In Python
import json
from gpxiltools import GpxilFile
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
print(g.metadata["reference"]["name"], g.sample_id)
for c in g: # each contig: its name, length and planes
print(c.name, c.length, c.planes, c.entry.get("encoding", {}).get("records"))
print(json.dumps(g.metadata["sample"], indent=2))
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
Check a gpxil file
gpxil validate checks a file against the gpxil specification, so that you – and anyone you send it to – can trust what it says. It reads the whole file a piece at a time, so it needs little memory even for a whole genome.
What it checks
- The metadata, against the specification’s JSON schema: the keys a file must have, and the right kind of value in each.
- The pages: that every contig has the pages its metadata names, in the right order, of the right size and type, with zero padding after the last base.
- The codes: that every value of the
ref, gt and flags planes is one the specification allows, that no haplotype claims as a variant the reference’s own base, and that phase sets are only on phased bases.
- The sidecar: that it reads back, that each base flagged as followed by an insertion has exactly one inserted sequence and the other way round, and that every complex variant has its entry.
- The checksums, if you ask: it spells out the reference plane’s bases and compares their MD5 and refget checksums with the metadata’s, which proves that the reference is the one the file names.
The form
- The gpxil file
- The file to check.
- Recompute the sequences’ checksums
- On by default. It reads every base of the reference plane: a few seconds for a chromosome kept here, and longer for one read from the web, which has to be downloaded (chromosome 1 is 67 MB). Untick it for a quick check.
What it says
Valid, with the number of contigs checked, or Not valid with a table of what is wrong. An error is something a reader can’t rely on: the file doesn’t follow the specification. A warning is worth knowing about but doesn’t make the file invalid. A file that isn’t gpxil, such as a TIFF picture, fails at once: its first page has no gpxil metadata. It writes no files.
In the terminal
gpxil validate /data/examples/worked-example.gpxil.tif
gpxil validate --no-digests /data/examples/HG002_chr17.gpxil.tif
It ends with exit status 0 for a valid file and 1 for one that isn’t, so a script can use it: gpxil validate FILE && echo fine.
In Python
from gpxiltools.validate import validate
report = validate("/data/examples/HG002_chr17.gpxil.tif", check_digests=False)
print("valid" if report.ok else "not valid", report.checked)
for e in report.errors:
print("error:", e)
for w in report.warnings:
print("warning:", w)
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- refget sequence identifiers
- Yates AD, Adams J, Chaturvedi S, Davies RM, Laird M, Leinonen R, et al. (2021) Refget: standardized access to reference sequences. Bioinformatics 38(1): 299–300. doi:10.1093/bioinformatics/btab524
Count what a file holds
gpxil stats counts what a gpxil file holds, for every contig or for the regions you give: how much of the genome was called, the person’s substitutions, insertions and deletions, how much is phased, and the quality and depth of the calls. It reads the planes a piece at a time, so a whole chromosome takes little memory and about ten seconds here (a few on your own computer), and the counts of regions add up to those of the whole.
What it counts
- reference
- The reference’s bases: A, C, G and T, with GC content (C and G over all four), N, gaps and soft-masked (repeat) bases.
- called
- Bases where every haplotype is known; not called, where neither is (UNKNOWN, as outside the regions called with confidence); partly called, where one is and one isn’t. A base with one copy (haplotype 2 ABSENT, as on chrX of a man) counts its one haplotype.
- substitutions
- Bases where a haplotype carries another base and none is unknown: homozygous where every haplotype carries the same one, heterozygous otherwise. Each base carried is a transition (A↔G, C↔T) or a transversion; their ratio, Ti/Tv, is about 2 for a whole human genome, and much lower in a call set with many errors.
- insertions, deletions
- A run of deleted bases on a haplotype is one deletion, homozygous when the other haplotype has the same run (or there is one copy); an insertion after a base likewise, by its inserted bases. Each comes with the bases inserted or deleted. An event belongs to the region it starts in, with all its bases.
- complex
- Complex variants (COMPLEX), such as inversions, by their entries in the sidecar.
- phasing
- The share of heterozygous substitutions that are phased, and the phase sets: how many, and their N50 and largest span, from their first to their last phased base.
- genotype quality, read depth
- The median, quartiles and mean of the
gq and dp planes, over the bases that have a value.
- filtered
- Bases covered by a call that didn’t pass its filters.
A substitution is counted once at its base: a site where the two haplotypes carry two different other bases is one heterozygous substitution, with two bases towards Ti/Tv. Two neighbouring deletions on the same haplotype are one run, so one deletion. So the counts can differ slightly from those of tools that count VCF records.
The form
- The gpxil file
- Any gpxil file. One without a sample has only its reference counted.
- Regions
- One or more regions, separated by spaces: a contig, CONTIG:START-END counting from 1, or CONTIG:POS for one base. Leave it empty for every contig. With several, a table compares them, and the counts of all of them together follow, each base counted once where regions overlap. In a file of windows, positions are the chromosome’s, and a chromosome’s name counts each of its windows.
- Its reference
- Only for a file written without its reference plane: the reference to count its bases and work out Ti/Tv with. Without it, the rest is counted.
The Viewer counts the same for the region in view, under its key (The Viewer).
In the terminal
gpxil stats /data/examples/HG002_chr17.gpxil.tif chr17:7,661,779-7,687,546
gpxil stats /data/examples/HG002_chr17.gpxil.tif chr17:1-20,000,000 chr17:20,000,001-40,000,000
gpxil stats /data/examples/HG002_chr17.gpxil.tif --json | head -30
In TP53, HG002 has 3 heterozygous and 11 homozygous substitutions, one heterozygous and one homozygous insertion, and six deletions; 970 bases weren’t called. --json gives every number to programs, with the same names as in Python.
In Python
from gpxiltools import GpxilFile
from gpxiltools.stats import format_stats, stats
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
counts = stats(g, [("chr17", 7_661_778, 7_687_546)]) # (0-based, the end not included)
print(format_stats(counts))
tp53 = counts["regions"][0]
print(tp53["substitutions"], tp53["calls"]["not_called"])
stats(g) counts every contig; counts["total"] holds all the regions together. region_tally counts one region and returns a tally that adds to others with +.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
Make a gpxil file (encode)
gpxil encode turns a reference genome, and one person’s variants from a VCF, into a gpxil file. Every base of the reference becomes a pixel of the ref plane, and every call in the VCF is written base by base into the gt and flags planes, as the specification sets out, so that any two programs that follow it make the same planes from the same VCF.
The form
- The reference genome
- A FASTA file (plain, gzip or bgzip), or a gpxil file with its reference plane, such as a chromosome of GRCh38 from
/data/grch38. From a gpxil file, only the contigs you encode are read; a FASTA file is read a record at a time.
- The person’s variants (VCF)
- A VCF (or
.vcf.gz) with the person’s genotypes, version 4.2 or later. Without one, the file holds the reference alone.
- Sample
- For a VCF of several people, the column to encode; by default, the first.
- Regions called with confidence (BED)
- Bases outside these regions are UNKNOWN – not called – rather than taken to be the reference. Give one whenever you have it: a VCF of variant sites says nothing about the bases it doesn’t list. A BED file with the same name as the VCF is chosen for you.
- Only these contigs
- Contigs of the reference, separated by commas, such as
chr17. By default, all of the reference’s contigs; one with no calls in the VCF, and no callable regions, is stored as reference only.
- Only these regions, as windows
- Instead of contigs, regions such as
chr17:7,600,001-7,750,000, counting from 1, separated by spaces. Each becomes a window (gpxil 0.3): a contig that holds just those bases and keeps the chromosome’s positions. Only the calls that reach into a region are applied, and only the reference’s bases there are read, so it takes a second or two and little memory – and the window holds just what the same stretch of the whole chromosome would. HG002’s variants around TP53, 150,000 bases, make a file of 50 kB (7 kB without the reference plane).
- Planes from the VCF
gt and flags (the default here), or all the VCF has: also its genotype qualities (gq), read depths (dp) and phase sets (ps), which take as much memory again.
- Leave out the reference plane
- A much smaller file, which names its reference by the checksums of its sequences. Reading its bases then needs the reference too.
- Sex chromosomes
- XY: chromosomes X and Y outside the pseudo-autosomal regions of GRCh38 have one copy, so haplotype 2 is ABSENT there. XX records the sex without changing anything.
- The reference’s name
- Such as GRCh38, recorded in the metadata. A gpxil file as the reference gives its own.
- Bases per row of each page
- The layout of the pictures, 8,192 by default. It changes nothing in the data – any reader can re-wrap a plane – but small rows make small strips, so a region reads quickly.
- Compression
- Deflate, which every TIFF reader supports, or none.
- Colours stored for image viewers
- The palette of the colour map stored with the
ref plane, so that an ordinary image viewer shows the file in colour. It doesn’t change the data.
- Save as
- The new file,
~/genomes/name.gpxil.tif by default.
What it writes
One gpxil file, and a line saying how many contigs and bases it holds, how big it is and how long it took. Under that may come notes, such as VCF contigs that the reference doesn’t have (skipped), or calls that conflict with each other (the later one wins, and the metadata counts them). How a VCF becomes gpxil explains exactly how each kind of call is written.
It holds each contig’s planes whole while it works – about 6 bytes a base with the planes gt and flags – so the biggest contig sets the memory needed. HG002’s variants on chromosome 17 (83 million bases) take about half a gigabyte, and about a minute in the browser (12 seconds on a computer’s own Python); chromosome 1 (249 million bases) would need about 1.5 GB, more than a browser tab may have. So in the browser, encode one chromosome at a time, with Only these contigs, and encode the biggest chromosomes, or a whole genome, on your own computer (On your own computer). A window needs memory for its own bases only, so a gene, or a few megabases around it, is quick anywhere.
In the terminal
gpxil encode /data/examples/worked-example.fa -v /data/examples/worked-example.vcf --width 4 -o ~/genomes/worked-example.gpxil.tif
gpxil encode /data/grch38/chr17.gpxil.tif -v /data/examples/HG002_TP53.vcf --callable /data/examples/HG002_TP53.bed --planes gt,flags --no-ref -o ~/genomes/HG002_TP53.noref.gpxil.tif
gpxil encode /data/grch38/chr17.gpxil.tif -v /data/examples/HG002_TP53.vcf --callable /data/examples/HG002_TP53.bed --planes gt,flags --region chr17:7,600,001-7,750,000 -o ~/genomes/HG002_TP53_chr17_7600001-7750000.gpxil.tif
| Option | |
-v VCF, -s SAMPLE | the variants, and which sample |
--callable BED | the regions called with confidence |
--haploid BED, --haploid-contig NAME | regions, or whole contigs, where the person has one copy |
--sex XY | one copy of chrX and chrY outside the pseudo-autosomal regions |
--contigs A,B, --ucsc-names | which contigs; rename 1–22, X, Y and MT to chr1 … chrM |
--region CONTIG:START-END | a window of the reference instead of whole contigs; give it again for more |
--no-ref, --planes gt,flags | leave out the reference plane; which planes from the VCF (without --planes, the command takes gq, dp and ps too when the VCF has them, unlike the form) |
--width N, --compression NAME, --level N | the layout and the compression (on your own computer, also zstd, lzw, lzma and packbits, with the imagecodecs package) |
--palette NAME, --reference-name NAME | the colours for image viewers; the reference’s name |
--invalid-to-n | turn characters of the FASTA that aren’t IUPAC codes into N, rather than stopping |
--bigtiff | BigTIFF from the start; otherwise gpxil switches to it by itself if the file might pass 4 GB |
In Python
from gpxiltools.encode import encode
summary = encode("/data/examples/worked-example.fa", "/home/user/genomes/worked-example.gpxil.tif",
vcf="/data/examples/worked-example.vcf", width=4, reference_name="worked example")
print(summary)
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- VCF, the variant call format
- Danecek P, Auton A, Abecasis G, Albers CA, Banks E, DePristo MA, et al. (2011) The variant call format and VCFtools. Bioinformatics 27(15): 2156–2158. doi:10.1093/bioinformatics/btr330
- GA4GH and the SAMtools team (hts-specs). The Variant Call Format specification, VCFv4.4. samtools.github.io/hts-specs/VCFv4.4.pdf
- refget sequence identifiers
- Yates AD, Adams J, Chaturvedi S, Davies RM, Laird M, Leinonen R, et al. (2021) Refget: standardized access to reference sequences. Bioinformatics 38(1): 299–300. doi:10.1093/bioinformatics/btab524
- IUPAC nucleotide codes
- Cornish-Bowden A (1985) Nomenclature for incompletely specified bases in nucleic acid sequences: recommendations 1984. Nucleic Acids Research 13(9): 3021–3030. doi:10.1093/nar/13.9.3021
Make a working copy (subset)
gpxil subset copies some contigs of a gpxil file, and if you like only some of its planes, into a new file: a working copy, such as one chromosome out of a whole genome. Each contig of a gpxil file stands on its own – its pages and its entry in the metadata – so nothing needs encoding again: the pages’ strips are copied as they are stored, without being decoded. It is quick, needs little memory, and works as well on a file you added to this browser, or one on the web. Given a region, such as a gene, it cuts that stretch out as a window, which keeps the chromosome’s positions.
What it is for
- One chromosome out of a whole genome. A person’s whole genome is about 850 MB as gpxil; chromosome 17 is 25 MB of it. Make the copy on your own computer and add it here (On your own computer), or add the whole genome here: the copy reads only the pages it copies.
- A smaller file. The reference plane is most of a file: without it, HG002’s chromosome 17 goes from 24.6 MB to 1.9 MB. The contigs keep the checksums of their reference, so any tool can be given it again (each tool has a field for it, and the Viewer borrows GRCh38’s from the Gpxil browser). Leaving out genotype quality and read depth (
gq, dp) saves a little more.
- A gene, or any stretch, as a file of its own. A region becomes a window: a contig that holds just those bases and says where they lie on the chromosome, so every tool, and the Viewer, still gives their positions as the chromosome’s. TP53 and 100,000 bases either side is 76 kB (12 kB without its reference plane), out of the 24.6 MB of chromosome 17. Only the strips that hold the region are read, so it takes a second, from a file here or on the web – the way to send someone a gene.
- The reference put back. A file written without its reference plane gets it again from a reference you give, which is checked against the checksums.
- Another compression. A file made elsewhere with Zstandard, which the browser can’t read, encoded again with deflate (on a computer with the imagecodecs package).
The form
- The gpxil file
- Any gpxil file: yours, an example, or a chromosome of GRCh38.
- Contigs
- The contigs to copy, by name, separated by spaces or commas (
chr17, or chr17 chr21); 17 finds chr17 too. Empty: all of them (unless you give regions). The copy keeps them in the file’s order. In a file of windows, a chromosome’s name copies all its windows.
- Regions, cut out as windows
- Regions such as
chr17:7,661,779-7,687,546, counting from 1 and including both ends, separated by spaces: each becomes a window. The positions are the chromosome’s, in a file of windows too, where a region is cut out of the window that holds it.
- Planes
- All the planes each contig has, or some of them: without genotype quality and read depth; the reference, genotype and flags only; or the reference only, without the sample. A plane a file doesn’t have is left out.
- Leave out the reference plane
- As
gpxil encode --no-ref: a file about a hundred times smaller.
- Put its reference plane back, from
- Only for a file written without one: a FASTA file, or a gpxil file with its reference plane, such as a chromosome of GRCh38 from
/data/grch38.
- Compression
- As it is (the default), so that the strips are copied without decoding, or encoded again.
- Save as
- A file name ending in
.gpxil.tif, in ~/genomes unless you choose otherwise.
A window holds exactly what the whole chromosome holds over its bases: every plane, the insertions that follow its bases, and its phase sets – and no more, so a deletion that runs past its end, say, stops at its end. It won’t leave out the phase sets (ps) of a contig or window with more than one, because its phased calls would then read as one phase set.
What it writes
A gpxil file with the contigs and planes chosen, each contig with its entry in the metadata – its checksums, how it was encoded, how many records – and a record, subset, of the file it came from. A window is named for the stretch it holds, such as chr17:7661779-7687546, and its entry says where it lies, "window": {"of": "chr17", "start": 7661778, …} with the chromosome’s length and checksums; a file with windows follows version 0.3 of the specification (The gpxil format). It says how many contigs, bases and megabytes it wrote, and whether the strips were copied as stored. Check it, look at it in the Viewer, or count what it holds.
In the terminal
gpxil subset /data/examples/HG002_chr17.gpxil.tif chr17 --no-ref -o ~/genomes/HG002_chr17.noref.gpxil.tif
gpxil subset ~/genomes/HG002_chr17.noref.gpxil.tif --planes ref,gt,flags --reference /data/grch38/chr17.gpxil.tif -o ~/genomes/HG002_chr17.three-planes.gpxil.tif
gpxil info ~/genomes/HG002_chr17.three-planes.gpxil.tif
gpxil subset /data/examples/HG002_chr17.gpxil.tif chr17:7,661,779-7,687,546 -o ~/genomes/HG002_chr17_7661779-7687546.gpxil.tif
gpxil info ~/genomes/HG002_chr17_7661779-7687546.gpxil.tif
gpxil fasta ~/genomes/HG002_chr17_7661779-7687546.gpxil.tif --which 1 --region chr17:7676101-7676200
On your own computer, chromosome 17 out of a whole genome: gpxil subset HG002.gpxil.tif chr17 -o HG002_chr17.gpxil.tif. The input can be a URL too, and then only the pages copied are downloaded – for a window, only the strips that hold it.
In Python
from gpxiltools import GpxilFile
from gpxiltools.subset import subset
summary = subset("/data/examples/HG002_chr17.gpxil.tif", "/home/user/genomes/HG002_chr17.noq.gpxil.tif",
["chr17"], planes=["ref", "gt", "flags", "ps"])
print(summary)
# TP53 as a window: its positions are still chromosome 17's
subset("/data/examples/HG002_chr17.gpxil.tif", "/home/user/genomes/TP53.gpxil.tif", ["chr17:7,661,779-7,687,546"])
with GpxilFile("/home/user/genomes/TP53.gpxil.tif") as g:
w = g.contigs[0]
print(w, g[w].parent, g[w].start) # chr17:7661779-7687546 chr17 7661778
c, s, e = g.locate("chr17", 7676153, 7676154) # chr17:7,676,154, in the window
print(c.read("gt", s, e))
GpxilWriter.copy_contig does the copying, a contig at a time, and GpxilWriter.cut_contig cuts a window, for your own programs.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
Compare two genomes
gpxil compare compares two gpxil files of one person base by base: two sets of calls, say, or two versions of a benchmark. Both are laid out on the same reference, base for base, so they are compared as arrays, without matching VCF records: what is the same in both, what only one has, and what the two only write differently. With the second taken as the truth, it gives each kind of variant’s precision and recall, as benchmarking tools do. A whole chromosome takes a few seconds on a computer, and about ten here.
How it compares
- base by base
- At each base called in both files, the two genotypes are compared as pairs of alleles in either order: A/G is the same as G/A, phased or not, and one copy (on a man’s X, say) is the same as two copies of the same allele. An allele is what one haplotype has at a base – the reference’s base, another base, nothing (deleted) or a COMPLEX allele – with any bases inserted after it.
- written differently
- The same change can often be written in more than one way: an insertion in a run of As can be anchored at either end of the run, and a deletion in a repeat can take any of its units. Where the files differ, the bases near each other (no more than 50 apart: the gap) are taken together, widened over any repeat their insertions and deletions could move along, and each file’s two haplotypes are spelled out over them, its heterozygous pieces tried either way round. If both files can spell the same pair of sequences, they only write it differently, and it counts as the same.
- events
- Each file’s substitutions, insertions, deletions and COMPLEX alleles, counted as gpxil stats counts them, are then looked for in the other: found if every base they cover is the same in both (or written differently), not in the other, or there with another genotype. Events over bases the other file didn’t call aren’t compared.
- precision and recall
- With B as the truth, precision is the share of A’s events found in B, recall the share of B’s events found in A, and F1 is the two together (their harmonic mean).
- calls outside the confident regions
- A file made with a BED file of the regions called with confidence keeps the VCF’s calls outside them too, each on its own among bases not called. They aren’t compared, as a benchmark’s own rules say. Tick Also compare the calls outside… to compare them as well.
The form
- A, and B (the truth)
- Two gpxil files of one person on the same reference: the genome to check, and the one to check it against. Either can be without its reference plane, but to spell stretches out one of them needs it, or the reference is given (a field for it appears when neither has one).
- Regions
- One or more regions, separated by spaces: a contig, or CONTIG:START-END counting from 1. Empty: every contig the two have. Files of windows compare too, at the chromosome’s positions, where both hold the bases: windows of one file that overlap or touch are read as one stretch, so each base is compared once.
- Draw a region
- A region to draw, one pixel a base, with its bases per row and pixels per base as in gpxil render; TP53 to begin with. Clear it for no picture.
- The gap
- How far apart two differences can be and still be spelled out together: 50 bases. Repeats are taken in whole, however long, so it matters only for differences far apart outside a repeat.
What it gives
A table, as gpxil compare prints it: the bases called in both, in only one or in neither; the variant bases that are the same (and of those, how many are only written differently), only in A, only in B, or there with another genotype; and for each kind of event, how many each file has, found, not found and with another genotype, with precision, recall and F1. Then the first stretches where the two differ, with A’s and B’s genotypes there, and the picture, with its key:
| Colour | The bases |
| the reference, faded | the same in both, without a variant |
| green | the same variant in both |
| teal | the same, written differently |
| red | a variant only in A |
| blue | a variant only in B |
| orange | a variant in both, but another genotype |
| grey | not called in both |
Look at the first difference in the Viewer opens A there.
HG002’s two benchmarks
Genome in a Bottle’s benchmarks of HG002 have been made twice: version 4.2.1 from short and linked reads, and version 5.0q from a whole assembly of his genome. The examples hold chromosome 17 of each. Compared in the regions both call with confidence (83.7 % of the chromosome), 113,164 bases have a variant in one or the other, and all but 4 are the same in both; 413 of them are only written differently. For substitutions, precision is over 99.99 % and recall 100 %; for insertions and deletions, both are 100 %. The two stretches that differ hold four substitutions only version 4.2.1 has (A family says more).
In the terminal
gpxil compare /data/examples/HG002_chr17.gpxil.tif /data/examples/HG002_chr17_v5.0q.noref.gpxil.tif \
-o ~/results/HG002_v4_v5_TP53.png --draw chr17:7,661,779-7,687,546 --width 256 --scale 2
gpxil compare /data/examples/HG002_chr17.gpxil.tif /data/examples/HG002_chr17_v5.0q.noref.gpxil.tif --region chr17:7,661,779-7,687,546 --json | head -20
--region (one or more) compares regions rather than every contig; --gap sets the gap; --all-calls compares the calls outside the confident regions too; --json gives every number to programs.
In Python
from gpxiltools import GpxilFile
from gpxiltools.compare import compare_pair, format_compare
with (
GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as a,
GpxilFile("/data/examples/HG002_chr17_v5.0q.noref.gpxil.tif") as b,
):
result = compare_pair(a, b, [("chr17", 0, 40_000_000)]) # (0-based, the end not included)
print(format_compare(result))
subs = result["total"]["events"]["substitutions"]
print(subs["precision"], subs["recall"], result["total"]["examples"][:2])
compare_pair(a, b) compares every contig the two have; stretches=[] collects every stretch where they differ, and picture([a, b], contig, start, end) draws a region.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
A child and both parents (trio)
With --father and --mother, gpxil compare checks a child’s genome against both parents’, base by base: whether each of the child’s genotypes can be one allele from each parent (or a Mendelian error), which variants neither parent has (candidate new mutations), where each of the child’s variants came from, and, where the parents are phased, which of each parent’s two haplotypes the child received, and where that changes (crossovers).
What it finds
- Mendelian errors
- At each base called in all three, the child’s two alleles must be one from the father and one from the mother; where the child has one copy, as on a son’s X, it must come from either. Where it can’t, the stretch around the error, with every variant of the three near it, is spelled out first, as the three may only write the same thing differently (an insertion anchored at the other end of a repeat, say): if the child’s two sequences there can be one of the father’s and one of the mother’s, it isn’t an error. What is left are the Mendelian errors, counted by sites (runs of bases) and bases.
- candidate new mutations
- Errors where the child has an allele that neither parent has. In a family’s calls, most errors are mistakes in the calls; a few are mutations new in the child.
- where each variant came from
- Where the child has a variant and only one way of taking an allele from each parent fits, it came from the father, from the mother, or from both (a homozygous variant). Where both ways fit – the child and both parents heterozygous for it, say – it can’t be told.
- crossovers
- Where a parent’s calls are phased, the child’s genotype at a base where that parent is heterozygous often shows which of the parent’s two haplotypes the child received. Along a phase set, a change from one to the other is a crossover in that parent’s meiosis, or an error in the phasing or in the calls: so a change counts as a crossover only where at least three sites in a row back each side. With unphased parents, it says so.
- calls outside the confident regions
- As with two genomes, calls a file keeps outside its regions called with confidence aren’t compared, unless you tick Also compare the calls outside…. With them, a trio of benchmarks shows many more errors, nearly all where one of the three isn’t called with confidence.
The form
- The child, the father, the mother
- Three gpxil files on the same reference, each with a sample. Any of them can be without its reference plane, as the parents in the examples are; one needs it to spell stretches out, or the reference is given.
- Regions, Draw a region, the gap
- As for two genomes.
What it gives
A table, as gpxil compare prints it: the bases called in all three; the child’s variant bases, by where they came from; the Mendelian errors and how many are candidate new mutations; the stretches consistent once spelled out; and, for each parent, the crossovers. Then the first errors, with the three genotypes at each and whether it is a candidate new mutation, and the picture, with its key:
| Colour | The child’s bases |
| the reference, faded | no variant |
| blue (light blue: his haplotype 2) | a variant from the father |
| pink (light pink: her haplotype 2) | a variant from the mother |
| purple | a variant from both parents |
| olive | a variant from either: it can’t be told |
| teal | consistent, written differently |
| red | a Mendelian error |
| gold | a candidate new mutation |
| grey | not called in all three |
The light colours appear only where that parent is phased, so a crossover shows as a change from dark to light along the chromosome. Look at the first error in the Viewer opens the child there.
HG002 and his parents
HG003 and HG004 are HG002’s father and mother, and Genome in a Bottle has benchmarked all three. On chromosome 17, 84.3 % is called with confidence in all three, and of HG002’s 106,558 variant bases there, 28,419 came from his father, 27,062 from his mother and 40,337 from both; for 10,716, it can’t be told. 22 sites are Mendelian errors, all inside the three benchmarks’ regions: 21 substitutions and a deleted base that HG002 has and neither parent has – candidate new mutations (A family says more). The parents’ benchmarks aren’t phased, so no crossovers can be seen.
In the terminal
gpxil compare /data/examples/HG002_chr17.gpxil.tif \
--father /data/examples/HG003_chr17.noref.gpxil.tif --mother /data/examples/HG004_chr17.noref.gpxil.tif \
-o ~/results/HG002_trio_TP53.png --draw chr17:7,661,779-7,687,546 --width 256 --scale 2
In Python
from gpxiltools import GpxilFile
from gpxiltools.compare import compare_trio, format_compare
errors = []
with (
GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as child,
GpxilFile("/data/examples/HG003_chr17.noref.gpxil.tif") as father,
GpxilFile("/data/examples/HG004_chr17.noref.gpxil.tif") as mother,
):
result = compare_trio(child, father, mother, errors=errors)
print(format_compare(result))
print(len(errors), "Mendelian errors:", [e["position"] for e in errors if e["new"]][:5], "...")
errors=[] collects every Mendelian error (the result lists the first 20), each with where its site begins and ends and the three genotypes; result["total"]["haplotypes"] holds each phased parent’s crossovers.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
Find runs of a composition
gpxil runs finds the stretches of a genome made only of some bases: of A and G, as Guenter Albrecht-Buehler’s pure GA-sequences are, of C and T, of A and T, or of one base. It reads the reference plane a piece at a time, so a chromosome takes a few seconds, and writes the runs as a BED file, ready for the other tools that look for patterns: stack them, count their substitutions, or add them to the genome as a plane.
What a run is
- its bases
- Given in IUPAC letters (The codes): AG or R for purines, CT or Y for pyrimidines, AT or W, CG or S, A for runs of one base, and so on. A base of the reference belongs if its own letter does: an R belongs to AG, an N only to ACGT. Soft masking doesn’t matter.
- as long as it can be
- A run is maximal: the bases either side of it don’t belong, or there are none. Runs shorter than the shortest you ask for are left out: 51 bases by default, since Albrecht-Buehler’s pure GA-sequences are more than 50 bases long.
- on both strands
- The other strand’s bases are the complements of this one’s, so a run of A and G there is a run of C and T here. With both strands, those are found too, and written with strand
-. A set that is its own complement, such as AT or CG, has the same runs on both strands: they are written once, without a strand (.).
- in a file of windows
- Windows that overlap or touch are read as one stretch, at the chromosome’s positions, and where a stretch ends, a run ends with it.
The form
- The gpxil file
- Any gpxil file with its reference plane, or one without it, with its reference given (a field for it appears).
- Of these bases
- The set, in IUPAC letters: AG to begin with.
- At least this many bases
- The shortest run to keep: 51.
- On both strands
- Ticked: the set’s runs on the other strand too.
- Regions
- One or more, separated by spaces: a contig, or CONTIG:START-END counting from 1. Regions that overlap are read once, as one. Empty: every contig.
- Save as
- A BED file, named after the genome and the set:
~/results/HG002_chr17_AG_runs.bed.
What it gives
A BED file, a run a line: its contig, its start (counting from 0) and end, the set (AG), its length as the score (up to 1000, the most a BED score can be) and its strand. The tool also says how many runs there are on each strand, how many bases they cover, how long they are – the median, the 90th and 99th percentiles and the longest – and how many there are a megabase of A, C, G and T. Stack them and Their substitutions take the BED file on to the next tools.
Chromosome 17’s pure GA-sequences
Chromosome 17 of GRCh38 has 1,561: 739 runs of A and G on the + strand and 822 on the − strand (runs of C and T on this one), 140,621 bases in all, half of them 71 bases long or less and the longest 574: 18.8 a megabase. By chance, with half the bases purines, a stretch of 51 bases would be all purines once in 2⁵¹ – about 2 × 10¹⁵ – so the whole genome would have none. Albrecht-Buehler counted 19,139 in the human genome of 2008, and 1,667 on chromosome 1; GRCh38 has 22,726 runs of A and G and 22,955 of C and T, as many on each strand as Chargaff’s second rule expects (Strand symmetry). Pure GA-sequences follows them through the other tools.
In the terminal
gpxil runs /data/examples/HG002_chr17.gpxil.tif --of AG --both-strands -o ~/results/GA.bed
gpxil runs /data/examples/HG002_chr17.gpxil.tif chr17:7,661,779-7,687,546 --of A --min 15
gpxil runs /data/examples/HG002_chr17.gpxil.tif --of W --min 200 -o ~/results/AT-rich.bed --json
Without -o, the runs go to the terminal; --json gives the counts to programs; --reference gives the reference of a file written without one.
In Python
from gpxiltools import GpxilFile
from gpxiltools.runs import RunFinder
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
runs = list(RunFinder(g, of="AG", min_length=51, both_strands=True).runs())
print(len(runs), "runs; the longest:", max(runs, key=lambda r: r.length))
print(sum(r.strand == "-" for r in runs), "on the - strand")
Each run is a Run(sequence, start, end, strand), counting from 0; RunFinder(g, [g.region_of("chr17:1-1000000")], …) looks only there, and write_bed writes runs as the command does.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Pure GA-sequences, and their flanks
- The correction replaces the abstract’s “160 times fewer” point mutations with “many”.
- Albrecht-Buehler G (2008) Properties and distribution of pure GA-sequences of mammalian genomes. PLoS ONE 3(11): e3818. doi:10.1371/journal.pone.0003818
- Albrecht-Buehler G (2009) Correction: Properties and distribution of pure GA-sequences of mammalian genomes. PLoS ONE. doi:10.1371/annotation/7126ab55-ca2a-4202-9e66-fac899bd52ee
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- IUPAC nucleotide codes
- Cornish-Bowden A (1985) Nomenclature for incompletely specified bases in nucleic acid sequences: recommendations 1984. Nucleic Acids Research 13(9): 3021–3030. doi:10.1093/nar/13.9.3021
Stack features, lined up
gpxil stack draws features one a row – runs, repeats, genes, the intervals of any BED file – each with the bases before and after a point of it, its start, end or centre, read on its own strand. Lined up, what the features share stands out as stripes running down the picture. It is how Guenter Albrecht-Buehler saw Alu elements line up before the pure GA-sequences that begin with a poly(A).
How the rows are made
- the anchor
- Each feature’s start, end or centre, on its strand: the start of a feature on the − strand is its right-hand end. Each row holds the bases before the anchor and those from it on, so lined up at their starts, the column just right of the anchor’s mark holds the first base of every feature, and lined up at their ends, the first base after each.
- read 5′ to 3′
- A feature on the − strand is read as the reverse complement, and the sample’s variants with it: a substitution of C by T there is drawn as one of G by A. So a poly(A) that starts features on either strand starts every row. A feature without a strand is read on the + strand.
- beyond the sequence
- Where a row runs off the end of its sequence, or out of the windows of a file of windows, it has no bases: paper.
- sorted
- As the features come; by position; by length; by score (a repeat’s divergence from its family’s consensus, which grows with its age, so the oldest come first); by name; or by the run of some bases from the anchor, such as the poly(A) a GA-sequence begins with, allowing a few other bases in it, as Albrecht-Buehler sorted his. Lengths, scores and runs go longest first, unless you sort the other way.
- binned
- A chromosome has thousands of repeats of a family: with rows to a pixel row above 1, each pixel row is the mean colour of that many rows, so the stripes they share remain and the picture fits a screen.
The form
- The gpxil file
- The genome the rows are read from.
- The features
- A BED file – the runs of Find runs, the examples’
chr17_genes.bed, any of your own – or a RepeatMasker table: UCSC’s rmsk table, as in the examples’ rmsk_chr17.txt.gz, or RepeatMasker’s own .out. Plain or gzipped. A repeat’s name is its class, family and name: SINE/Alu/AluSx.
- Only features whose name matches, of at least this many bases
- A regular expression looked for in the names (
^SINE/Alu/ for Alu elements, /L1 for L1s, a gene’s name), and the shortest feature to keep: 250 bases leaves out the fragments of Alu elements.
- Line them up at; bases before the anchor; bases from it on
- Their starts, 400 bases before and 200 from it on, to begin with.
- Sort the rows
- By score for a RepeatMasker table, by the poly(A) for runs from Find runs, else as the features come.
- At most this many rows
- The first rows, once sorted.
- Show, colours
- The reference’s bases, or the sample’s genotype, haplotypes or variants, as Draw a region draws them, in any palette.
- Rows to a pixel row, pixels per base
- 1 and 1 to begin with.
- With a title…
- Ticked: the picture gets a title, a mark at the anchor, a ruler of bases from it, and a key.
- Each column’s bases as a table
- The profile: for each column, how many rows have A, C, G and T there (and, for a sample, how many have a substitution, a deletion or an insertion): the picture as numbers, to plot.
What it gives
The picture, shown under the result, and the profile if you asked for it. The tool says how many rows it drew, of how many features, and how they were sorted. Features on a contig the file doesn’t have are left out.
Two pictures of chromosome 17
Its Alu elements, by age (the first button): 43,132 Alu elements of 250 bases or more, lined up at their ends, oldest at the top, fifty to a pixel row. RepeatMasker counts an Alu’s poly(A) tail as part of it, so the tails are the pink band just before the ends: in the young elements at the bottom, long and pure; in the old ones at the top, shorter and mixed, as A’s have been substituted.
Its genes’ starts (the second): 1,187 protein-coding genes, longest first, with 1,000 bases either side of where each begins on its own strand, four to a pixel row. The starts sit in a band of C and G, blue and green – the CpG islands of promoters: 63 % of the bases just after the starts are C or G, against 50 % a kilobase before them – and the band is greener after the start than before it, as on a gene’s own strand G outnumbers C there (the GC skew is +0.035 in the first 500 bases, −0.01 before the start). The profile gives these counts column by column.
The GA-sequences’ own stack, sorted by their poly(A)s, is in Pure GA-sequences.
In the terminal
gpxil runs /data/examples/HG002_chr17.gpxil.tif --of AG --both-strands -o ~/results/GA.bed
gpxil stack /data/examples/HG002_chr17.gpxil.tif ~/results/GA.bed --sort run --labelled -o ~/results/GA-stack.png
gpxil stack /data/examples/HG002_chr17.gpxil.tif /data/examples/rmsk_chr17.txt.gz --match '^SINE/Alu/' --min-length 250 \
--anchor end --up 40 --down 80 --sort score --bin 50 --scale 2 --labelled -o ~/results/alu-ends.png --profile ~/results/alu-ends.tsv
--rows writes the features in the order drawn, each with its row and what it was sorted by; --ignore-strand reads every feature on the + strand; --cmyk writes a CMYK TIFF for print. Without -o, the profile goes to the terminal.
In Python
from gpxiltools import GpxilFile
from gpxiltools.features import read_features
from gpxiltools.stack import profile, rendering, stack
alus = read_features("/data/examples/rmsk_chr17.txt.gz", match="^SINE/Alu/", min_length=250)
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
st = stack(g, alus, anchor="end", up=40, down=80, sort="score")
print(len(st.rows), "rows;", st.ref.shape) # (a row of reference codes for each feature, 5' to 3')
rendering(st, mode="reference").save_png("/home/user/results/alu-ends-unbinned.png")
for col in profile(st)[30:46:3]: # (a dict for each column: its counts of A, C, G and T …)
print(col["offset"], col["commonest"], col["commonest_share"], col["soft_masked"])
stack(…, sample=True) reads the sample’s genotype too, for mode="variants"; binned and labelled make the command’s picture.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Pure GA-sequences, and their flanks
- The correction replaces the abstract’s “160 times fewer” point mutations with “many”.
- Albrecht-Buehler G (2008) Properties and distribution of pure GA-sequences of mammalian genomes. PLoS ONE 3(11): e3818. doi:10.1371/journal.pone.0003818
- Albrecht-Buehler G (2009) Correction: Properties and distribution of pure GA-sequences of mammalian genomes. PLoS ONE. doi:10.1371/annotation/7126ab55-ca2a-4202-9e66-fac899bd52ee
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- RepeatMasker, and the UCSC Genome Browser’s table of its repeats on GRCh38 (rmsk_chr17.txt.gz)
- Smit AFA, Hubley R, Green P (2013–2015) RepeatMasker Open-4.0. www.repeatmasker.org
- Kent WJ, Sugnet CW, Furey TS, Roskin KM, Pringle TH, Zahler AM, et al. (2002) The human genome browser at UCSC. Genome Research 12(6): 996–1006. doi:10.1101/gr.229102
- UCSC Genome Browser. RepeatMasker’s repeats on hg38 (GRCh38): the rmsk table. hgdownload.soe.ucsc.edu/goldenPath/hg38/database
- Ensembl (GRCh38 sequence, and genes from release 116)
- Yates AD, Austine-Orimoloye O, Azov AG, Barba M, Barnes I, Barrera-Enriquez VP, et al. (2026) Ensembl 2026. Nucleic Acids Research 54(D1): D1053–D1060. doi:10.1093/nar/gkaf1239
Substitutions in regions, by context
gpxil spectrum asks whether a person has more or fewer substitutions in some regions – repeats, GA-sequences, genes, any BED file – than their sequence leads one to expect. A base’s chance of changing depends on its neighbours, so the regions are compared with the rest of the genome context by context, with an exact interval for the ratio. It reads a person’s gpxil file and the regions, and counts a chromosome in a few seconds.
How it counts
- contexts
- A base with its neighbours: three bases by default, so the C of CG, which changes about ten times as often as other C’s, is a context of its own. Each context is read on the strand where its middle base is a pyrimidine, C or T, so a G that changes to A is counted as the C opposite changing to T: the 6 kinds and 96 contexts of mutational signatures. Or a purine, A or G, which keeps a GA-sequence on its own strand.
- expected
- For each context and each substitution in it, its rate in the background – every called base outside the regions, unless you give a BED file of your own, such as the regions’ flanks – times how many bases of the regions have that context. Their sum is what the regions would have if they changed as the rest of the genome does.
- the ratio, its interval and p
- Observed over expected. Its 95 % interval and p-value come from an exact binomial test of the regions’ share of all the substitutions counted, regions and background together, which allows for the background’s counts being a sample too. Each kind of substitution gets its own.
- on their own strands
- Ticked, the regions’ substitutions are also read on each feature’s strand, 12 kinds from A>C to T>G, against the background’s rates read blind to strands. A difference between a kind and its complement – A>G against T>C – is then an asymmetry between the strands, such as the one transcription leaves on genes.
- isolated
- Only substitutions with no other variant – a substitution, a deletion, an insertion – within so many bases count, in the regions and the background alike. In a repeat, a change of a unit is often written as several substitutions side by side; counted as isolated, it counts not at all, rather than as several.
- which bases count
- Those where the person’s genotype is known on both haplotypes, and the reference base and its context are all A, C, G and T. In a file written with regions called with confidence, the calls outside them are left out, as gpxil compare leaves them out; a BED file of bases to count narrows them further.
- also counted
- Transitions and transversions, heterozygous and homozygous alleles, and insertions and deletions a called base: a base counts once if a deletion begins there or an insertion follows it.
The form
- The person’s gpxil file
- With a sample. A file without its reference plane needs the reference too (a field for it appears).
- The regions
- A BED file, or a RepeatMasker table, with the features to keep: names that match, and the shortest (Stack features says how).
- Only substitutions with no other variant within
- Empty: all of them. 20 bases is what Pure GA-sequences takes.
- Read contexts with in the middle
- A pyrimidine, or a purine for runs of A and G from Find runs.
- Context
- 3 bases, 1 or 5.
- Also read each feature on its own strand
- For features with strands: genes, or runs found on both strands.
- Count only in these regions; the background; count only these bases too
- A region or contig of the file; a BED file of the background’s bases; a BED file of the bases to count.
- Also the counts by context, as a table
- Each context and substitution’s counts and rates, in the regions and the background, as a tab-separated file.
What it gives
The regions and the bases counted; the verdict – how many substitutions the regions have, how many their contexts lead one to expect, and the ratio with its interval and p; a table by kind (and, ticked, on the features’ own strands); then substitutions a kilobase, transitions over transversions, the homozygous share, and insertions and deletions, each in the regions and the background.
Two examples on chromosome 17
Alu elements. HG002 has 18,561 substitutions in chromosome 17’s 58,071 Alu elements: 1.43 a kilobase, against 1.14 elsewhere, a quarter more. But Alu elements are rich in CG, the most mutable context, and their contexts lead one to expect 16,671: 1.11 times (95 % 1.10–1.13). By kind, T>C stands out, at 1.54 times, and insertions and deletions come 2.1 times as often a base, most of them in the A-rich tails at the elements’ ends.
Genes, on their own strands. Inside chromosome 17’s protein-coding genes, mostly introns, HG002 has 0.83 times the substitutions their contexts predict. Read on each gene’s own strand, A>G runs at 0.98 times and its complement T>C at 0.84: the strands differ, as transcription treats them differently.
In the terminal
gpxil spectrum /data/examples/HG002_chr17.gpxil.tif /data/examples/rmsk_chr17.txt.gz --match '^SINE/Alu/'
gpxil spectrum /data/examples/HG002_chr17.gpxil.tif /data/examples/chr17_genes.bed --stranded --context 1 -o ~/results/genes-spectrum.tsv
gpxil spectrum /data/examples/HG002_chr17_v5.0q.noref.gpxil.tif /data/examples/chr17_genes.bed chr17:7,000,001-8,000,000 \
--reference /data/examples/HG002_chr17.gpxil.tif --isolated 20 --json | head -24
--all-calls keeps the calls outside the regions called with confidence; --json gives every number to programs.
In Python
from gpxiltools import GpxilFile
from gpxiltools.features import read_features
from gpxiltools.spectrum import count_spectrum
alus = read_features("/data/examples/rmsk_chr17.txt.gz", match="^SINE/Alu/")
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
counter = count_spectrum(g, alus)
r = counter.results()
s = r["substitutions"]
print(f'{s["observed"]:,} observed, {s["expected"]:,.0f} expected: {s["ratio"]} times (95 % {s["interval"][0]}-{s["interval"][1]})')
for kind, d in r["types"].items():
print(kind, d["ratio"], d["p"])
count_spectrum(g, features, regions, background=…, k=5, centre="purine", isolated=20, stranded=True) takes the form’s settings; counter.table() gives the counts by context, which write_table writes as the command does.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Mutational signatures: substitutions in their contexts (the 96 kinds)
- Alexandrov LB, Nik-Zainal S, Wedge DC, Aparicio SA, Behjati S, Biankin AV, et al. (2013) Signatures of mutational processes in human cancer. Nature 500(7463): 415–421. doi:10.1038/nature12477
- Transcription’s mark on the two strands of genes
- Green P, Ewing B, Miller W, Thomas PJ, NISC Comparative Sequencing Program, Green ED (2003) Transcription-associated mutational asymmetry in mammalian evolution. Nature Genetics 33(4): 514–517. doi:10.1038/ng1103
- RepeatMasker, and the UCSC Genome Browser’s table of its repeats on GRCh38 (rmsk_chr17.txt.gz)
- Smit AFA, Hubley R, Green P (2013–2015) RepeatMasker Open-4.0. www.repeatmasker.org
- Kent WJ, Sugnet CW, Furey TS, Roskin KM, Pringle TH, Zahler AM, et al. (2002) The human genome browser at UCSC. Genome Research 12(6): 996–1006. doi:10.1101/gr.229102
- UCSC Genome Browser. RepeatMasker’s repeats on hg38 (GRCh38): the rmsk table. hgdownload.soe.ucsc.edu/goldenPath/hg38/database
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
Add an annotation plane
gpxil annotate copies a gpxil file with an annotation as a plane of its own: RepeatMasker’s repeats, a BED file’s genes or regions, a bedGraph’s signal. The plane has a value at every base, like the genome’s own planes, so it lines up with the genome base for base and goes wherever the file goes: the Viewer draws it, Python reads it, and a working copy of a region cuts it out with the rest.
What a base holds
- a label
- From a RepeatMasker table, the repeat over the base: its family by default (
SINE/Alu, LINE/L1, Satellite/centr …), or its class (SINE), or its name (SINE/Alu/AluSx). From a BED file with names, the feature’s name. Each label is a number, 1, 2, 3 … in the order of their names, 0 where there is no feature, and the plane’s metadata keeps the labels: 8-bit numbers for up to 255 labels, 16-bit for more.
- where a feature is
- 1 there, 0 elsewhere: a BED file without names, or any file, as presence.
- a score or a value
- A feature’s score (a repeat’s divergence from its family’s consensus, in %), or a bedGraph’s value – replication timing, a Hi-C compartment’s eigenvector, any signal – as a 32-bit floating-point number, and NaN where there is none.
- where features overlap
- The shorter one is drawn, so a repeat inside another shows; of two as long, the later in the file.
The plane is an extension plane of the gpxil specification (section 5.6): its name is x- and letters, digits, - or _, and readers that don’t know it pass it by. The file’s own planes are copied as they are stored, without decoding them, so a chromosome takes a few seconds, and the plane adds little: chromosome 17’s repeats add 0.7 MB to its 24.6 MB. In a file of windows, each window gets the features over its own stretch of the chromosome.
The form
- The gpxil file
- Any gpxil file, with its reference plane or without.
- The annotation
- A RepeatMasker table (UCSC’s
rmsk table, or RepeatMasker’s .out), a BED file or a bedGraph file, plain or gzipped.
- The plane’s name
x-repeats for repeats, else x- and the file’s name.
- A base holds; a repeat’s label
- What fits the file, to begin with: labels for a RepeatMasker table or a BED file with names, values for a bedGraph. A repeat’s label is its family, unless you choose its class or name.
- Only features whose name matches, of at least this many bases
- As in Stack features.
- What it holds; the values’ units
- Kept in the plane’s metadata: said from the file if you leave them empty.
- Make the plane again if the file has it
- Otherwise a file that has the plane already is refused.
- Save the copy as
- A new file,
~/genomes/HG002_chr17.repeats.gpxil.tif for the first example: the tool never changes the file it copies.
What it gives
The copy, and a line saying what was added: the plane, what its bases hold, how many labels, and how many bases have a feature. Chromosome 17’s repeats give 58 labels over 43,047,843 bases, half the chromosome. Look at it in the Viewer opens the plane’s own view: each label in a colour of its own, a repeat class in one hue – SINE red, LINE blue, LTR teal, DNA transposons yellow, simple repeats violet, low-complexity sequence pink, satellites green – its families in lighter and darker shades of it, and paper where there is no feature. Point at a base to read its label. A plane of values is drawn from light to dark blue over the range in view. Any annotation plane of a file is in the Viewer’s Show list.
Once the first button has made the copy, look at the repeats around a GA-sequence that began as an Alu element’s tail: an AluJb, then a run of AGAA repeated, which RepeatMasker calls a simple repeat.
In the terminal
gpxil annotate /data/examples/HG002_chr17.gpxil.tif /data/examples/rmsk_chr17.txt.gz --plane x-repeats -o ~/genomes/HG002_chr17.repeats.gpxil.tif
gpxil annotate ~/genomes/HG002_chr17.repeats.gpxil.tif /data/examples/rmsk_chr17.txt.gz --plane x-repeat-age --as score --units % \
-o ~/genomes/HG002_chr17.repeats-age.gpxil.tif
gpxil info ~/genomes/HG002_chr17.repeats-age.gpxil.tif
viewer ~/genomes/HG002_chr17.repeats.gpxil.tif chr17:6,792,801-6,793,400 --plane=x-repeats
A second plane goes on the copy that has the first. gpxil subset … --planes ref,gt,flags,x-repeats keeps planes by name, and gpxil validate checks the new file.
In Python
from gpxiltools import GpxilFile
from gpxiltools.annotate import annotate
summary = annotate("/data/examples/HG002_chr17.gpxil.tif", "/home/user/genomes/HG002_repeats.gpxil.tif",
"/data/examples/rmsk_chr17.txt.gz", plane="x-repeats", log=lambda msg: None)
print(summary["labels"], "labels over", summary["bases_with_a_feature"], "bases")
with GpxilFile("/home/user/genomes/HG002_repeats.gpxil.tif") as g:
labels = g.metadata["planes"]["x-repeats"]["labels"] # value -> label
values = g["chr17"].read("x-repeats", 6_792_900, 6_793_300)
print(sorted({labels[str(v)] for v in values.tolist() if v}))
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- RepeatMasker, and the UCSC Genome Browser’s table of its repeats on GRCh38 (rmsk_chr17.txt.gz)
- Smit AFA, Hubley R, Green P (2013–2015) RepeatMasker Open-4.0. www.repeatmasker.org
- Kent WJ, Sugnet CW, Furey TS, Roskin KM, Pringle TH, Zahler AM, et al. (2002) The human genome browser at UCSC. Genome Research 12(6): 996–1006. doi:10.1101/gr.229102
- UCSC Genome Browser. RepeatMasker’s repeats on hg38 (GRCh38): the rmsk table. hgdownload.soe.ucsc.edu/goldenPath/hg38/database
- Ensembl (GRCh38 sequence, and genes from release 116)
- Yates AD, Austine-Orimoloye O, Azov AG, Barba M, Barnes I, Barrera-Enriquez VP, et al. (2026) Ensembl 2026. Nucleic Acids Research 54(D1): D1053–D1060. doi:10.1093/nar/gkaf1239
Strand symmetry: skews in windows
gpxil skew measures, in windows along a genome, how the bases of one strand compare with the other’s. Over long stretches, a strand has as many A as T and as many C as G, and each short word as often as its reverse complement: Chargaff’s second parity rule. Where a stretch breaks it, something has treated the two strands differently – replication, transcription, repeats inserted one way round, runs of one strand’s bases – and the windows show where.
The measures
| Measure | What it is |
gc | G and C over all four bases |
gc_skew | (G − C) / (G + C): above 0 where G outnumbers C on the + strand |
at_skew | (A − T) / (A + T) |
ry_skew | purines less pyrimidines, (A + G − C − T), over all four |
km_skew | keto less amino bases, (G + T − A − C), over all four |
asymmetry_k | for words of k bases: how far each word’s count and its reverse complement’s differ, over their counts (palindromes left out): 0 when the strands are alike, 1 when no word’s reverse complement is there |
cumulative_gc_skew | the GC skew summed along the sequence: in bacteria, it turns where replication changes direction |
The bases counted are the reference’s A, C, G and T, on the + strand; a word is counted in the window of its first base, if all its bases count. Windows are set at multiples of their size along each sequence, so the windows of two files, or of a region and its whole chromosome, line up. You can leave out soft-masked bases (repeats), or count only the bases of some features, each read on its own strand: then the skews of genes are those of their coding strands, and a base under genes on both strands counts on each.
The form
- The gpxil file
- Its reference is what is measured: the sample’s variants aren’t.
- Bases a window
- 100,000 to begin with.
- Regions
- Contigs or regions; empty, every contig.
- k-mers for the asymmetry
- Word lengths of 1 to 6, separated by commas: 1,2,3,4.
- Leave out soft-masked bases
- Repeats don’t count.
- Count only the bases of these features
- A BED file or a RepeatMasker table, read on each feature’s strand (unless you untick it), with the features to keep.
- Also a map; the map’s measures
- A picture: a track for each measure, a pixel a window. Skews run from blue, below 0, through grey to red, above it, to the 99th percentile of their size; GC content and asymmetries from light to dark blue; a window with nothing counted is white.
- Save the table as
- A tab-separated file, a window a line: its sequence, start and end, the bases counted, A, C, G and T, the measures, the other bases and the soft-masked ones.
What it gives
The table and the map, and the measures over all the bases counted.
Chromosome 17
Over its 82,920,204 bases of A, C, G and T, chromosome 17 is 45.3 % G and C; its GC skew is +0.003 and its AT skew −0.001, and the counts of its words differ from their reverse complements’ by 0.2 % for single bases and 1.9 % for words of four: its strands are alike, as Chargaff’s second rule says. Window by window, they differ more, and in one long stretch, from 22.8 to 26.7 Mb, much more: the asymmetry of words of four is 0.39 there, against a median of 0.055 in the chromosome’s other windows, and the GC skew +0.06. That is the centromere: 3.9 Mb of alpha satellite, a unit of 171 bases repeated over and over, 98.6 % of it the same way round, on the + strand, in RepeatMasker’s table.
Read on their coding strands, chromosome 17’s genes lean to G and T: GC skew +0.018, AT skew −0.042, the asymmetry of single bases 0.030, thirteen times the whole chromosome’s. It is the excess of G and T over A and C on genes’ coding strands that transcription leaves (Green and colleagues, 2003).
In the terminal
gpxil skew /data/examples/HG002_chr17.gpxil.tif -o ~/results/chr17-skew.tsv --picture ~/results/chr17-skew.png --show gc,gc_skew,at_skew,asymmetry_4
gpxil skew /data/examples/HG002_chr17.gpxil.tif --features /data/examples/chr17_genes.bed --stranded --json | head -20
gpxil skew /data/examples/HG002_chr17.gpxil.tif chr17:20,000,001-30,000,000 --window 1000000 --k 4 | cut -f 2,9,10,14
Without -o, the table goes to the terminal; --json gives the totals, by sequence and over all; --show chooses the map’s measures.
In Python
from gpxiltools import GpxilFile
from gpxiltools.skew import SkewCounter, iter_skew
counter = SkewCounter(window=1_000_000, ks=(1, 4))
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
rows = list(iter_skew(g, counter=counter)) # (a dict for each window)
top = max(rows, key=lambda r: r["asymmetry_4"] or 0)
print(top["start"], top["end"], top["asymmetry_4"], top["gc_skew"])
print(counter.totals()["all"])
picture(rows, ["gc_skew", "at_skew"], window=…) draws the map, and write_rows writes the table.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Chargaff’s second parity rule
- Rudner R, Karkas JD, Chargaff E (1968) Separation of B. subtilis DNA into complementary strands. 3. Direct analysis. Proceedings of the National Academy of Sciences of the USA 60(3): 921–922. doi:10.1073/pnas.60.3.921
- Transcription’s mark on the two strands of genes
- Green P, Ewing B, Miller W, Thomas PJ, NISC Comparative Sequencing Program, Green ED (2003) Transcription-associated mutational asymmetry in mammalian evolution. Nature Genetics 33(4): 514–517. doi:10.1038/ng1103
- Cumulative skews
- Grigoriev A (1998) Analyzing genomes with cumulative skew diagrams. Nucleic Acids Research 26(10): 2286–2290. doi:10.1093/nar/26.10.2286
- RepeatMasker, and the UCSC Genome Browser’s table of its repeats on GRCh38 (rmsk_chr17.txt.gz)
- Smit AFA, Hubley R, Green P (2013–2015) RepeatMasker Open-4.0. www.repeatmasker.org
- Kent WJ, Sugnet CW, Furey TS, Roskin KM, Pringle TH, Zahler AM, et al. (2002) The human genome browser at UCSC. Genome Research 12(6): 996–1006. doi:10.1101/gr.229102
- UCSC Genome Browser. RepeatMasker’s repeats on hg38 (GRCh38): the rmsk table. hgdownload.soe.ucsc.edu/goldenPath/hg38/database
Wrap a repeat at its period
gpxil period wraps a tandem repeat – a microsatellite, a GA-sequence – at its period, one unit a row, so its units line up in columns and a unit that differs stands out. The reference is drawn beside the person’s two haplotypes, so you can see which units they have changed, gained or lost.
How it is wrapped
- the period
- For each step up to 50 bases, the share of the stretch’s bases that equal the base a step further on. The period is the shortest step whose share is within 0.05 of the best, so a repeat of AAAG is period 4, not 8. A stretch with no clear period – the best share under 0.6 – still gets the best, with a note. Or you give the period.
- aligned
- Each haplotype is drawn at the reference’s positions, so its rows are the reference’s rows: a substituted base is outlined, a deleted base is a dash, a base not known is in the colour of N, and an insertion is a bar on the right edge of the base it follows. A haplotype that isn’t there, as the second where a man has one X, is blank.
- as sequences
- Each haplotype is its own sequence, insertions in and deletions out, wrapped at the same period, so a unit gained or lost shifts the units after it, and the haplotype ends lower or higher than the reference.
- as text
- The reference and the haplotypes side by side, a unit a line; a substituted base is in lower case, a deleted one a dash, and an insertion a
+ after the unit, with its bases listed below.
The form
- The gpxil file
- A person’s file, or a reference alone. A file without its reference plane needs the reference too.
- Region
- CONTIG:START-END, counting from 1, of up to 20,000 bases. For a chromosome 17 file, the GA-sequence of the first button, to begin with.
- Period
- Empty: found.
- The haplotypes
- Aligned, or as their own sequences.
- As
- A picture, 8 pixels a base in any palette, or text.
A GA-sequence that began as an Alu’s tail
At chr17:6,793,028-6,793,302, 275 bases of A and G begin in the tail of an AluJb element: RepeatMasker calls most of them a simple repeat, (AGAA)n. Their period is 4: 85 % of the bases equal the base a unit on. Genome in a Bottle’s v4.2.1 benchmark doesn’t call them; the newer v5.0q, made from a whole assembly of HG002’s genome, does, and he is heterozygous for 15 substitutions, 2 insertions and 2 deletions there. Wrapped, haplotype 1 is the reference but for an insertion of three units, AGAA AGAA AGAA. Haplotype 2 has all the substitutions, 15 bases in 13 units, and each of those units becomes one the repeat already has – AAAG becomes AGAG, AGAA becomes AGAG – as a unit copied over its neighbour would; it also has an insertion of five units and two deletions of two bases. As sequences, haplotype 1 is 12 bases longer than the reference, and haplotype 2 16. Pure GA-sequences counts how often that happens.
In the terminal
gpxil period /data/examples/HG002_chr17_v5.0q.noref.gpxil.tif chr17:6,793,028-6,793,302 --reference /data/examples/HG002_chr17.gpxil.tif \
-o ~/results/GA-period.png
gpxil period /data/examples/HG002_chr17_v5.0q.noref.gpxil.tif chr17:6,793,028-6,793,302 --reference /data/examples/HG002_chr17.gpxil.tif \
--layout sequence | head -12
--period gives the period, --max-period the longest to look for, and --scale and --palette the picture’s squares and colours. Without -o, the text goes to the terminal.
In Python
from gpxiltools import GpxilFile
from gpxiltools.period import best_period, panels, text
with GpxilFile("/data/examples/HG002_chr17_v5.0q.noref.gpxil.tif", reference="/data/examples/HG002_chr17.gpxil.tif") as g:
c, s, e = g.view(*g.locate(*g.region_of("chr17:6793028-6793302"), join=True))
period, share, shares = best_period(c.read("ref", s, e))
ps = panels(c, s, e) # (the reference, then each haplotype)
print(period, round(share, 2), {k: round(v, 2) for k, v in shares.items() if k <= 8})
print(text(ps, period)[:300])
picture(ps, period, scale=8, title=…) draws the picture, a Pillow image.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
- RepeatMasker, and the UCSC Genome Browser’s table of its repeats on GRCh38 (rmsk_chr17.txt.gz)
- Smit AFA, Hubley R, Green P (2013–2015) RepeatMasker Open-4.0. www.repeatmasker.org
- Kent WJ, Sugnet CW, Furey TS, Roskin KM, Pringle TH, Zahler AM, et al. (2002) The human genome browser at UCSC. Genome Research 12(6): 996–1006. doi:10.1101/gr.229102
- UCSC Genome Browser. RepeatMasker’s repeats on hg38 (GRCh38): the rmsk table. hgdownload.soe.ucsc.edu/goldenPath/hg38/database
Draw a region as a picture
gpxil render draws a region of a gpxil file as a picture, one pixel (or a square of pixels) a base: a PNG for the screen, or a TIFF in RGB or in CMYK for print. The colours come from a palette when the picture is drawn; the file holds codes, not colours, which is why the same file gives exact colours on a screen and in ink.
The form
- The gpxil file
- Any gpxil file with a reference plane, or one written without it together with its reference (below).
- Region
- A contig, or
CONTIG:START-END, counting from 1 and including both ends, such as chr17:7,661,779-7,687,546. When you choose a file, the form suggests TP53 for a file with chromosome 17, or else a contig. In the browser, a picture may have up to 25 million bases.
- Show
- reference: the reference’s bases. genotype: the person’s two bases at once, a heterozygous base as the mix of its two colours, a base deleted from both as paper. haplotypes: the two haplotypes in two rows of pixels for each row of bases, haplotype 1 above. variants: the faded reference with the variants on it: heterozygous orange, homozygous red, a deletion dark, a base followed by an insertion green, no call grey, a complex variant purple. A file without a sample can only show the reference.
- Colours
- classic, okabe-ito, gpxi or process (The palettes).
- Bases per row
- By default the file’s own width (8,192); for a region of 200,000 bases or fewer, the form suggests 256. A width equal to a repeat’s period lines the repeat up in columns.
- Pixels per base
- Each base as a square of this many pixels a side; the form suggests 4 for a region of 50,000 bases or fewer, so that each base can be seen. The whole picture may have up to 64 million pixels in the browser.
- Layout
- rows, like text, or a Hilbert curve, which folds the region into a square so that bases next to each other in the genome stay next to each other in the picture. Not for the haplotypes view.
- Picture
- PNG for the screen; TIFF in RGB; or TIFF in CMYK for print, with each colour given directly in the palette’s ink values.
- Dots per inch to record
- The resolution written into the picture, which tells printing software how big to print it.
- Mark where insertions are
- In the haplotypes view, a base that an insertion follows is magenta.
- Its reference
- Only for a file written without its reference plane: the same reference, a FASTA file or a gpxil file with its reference plane. For GRCh38, the form fills in the chromosome from
/data/grch38, and gpxil checks it against the file’s checksums.
- Save as
- The picture, named after the file, the region and the view, in
~/results.
What it writes
One picture, and a line giving its size in pixels and the region. A PNG is an indexed-colour picture: small, and exact.
In the terminal
gpxil render /data/examples/HG002_chr17.gpxil.tif chr17:7661779-7687546 -o ~/results/TP53.png --mode variants --width 256 --scale 4
gpxil render /data/examples/worked-example.gpxil.tif chrT -o ~/results/worked-example.png --mode haplotypes --width 4 --scale 16 --mark-insertions
| Option | |
FILE REGION -o PICTURE | the file, the region, and the picture to write: .png, or .tif |
--mode reference|genotype|haplotypes|variants | the view (genotype if you don’t say) |
--palette NAME | classic, okabe-ito, gpxi or process |
--width N, --scale N | bases per row; pixels per base, each way |
--layout raster|hilbert | rows, or a Hilbert curve |
--cmyk, --dpi N | a CMYK TIFF for print; the resolution to record |
--mark-insertions | in the haplotypes view, mark the bases insertions follow |
--reference FILE | the reference, for a file written without one |
In Python
from gpxiltools import GpxilFile
from gpxiltools.render import render
with GpxilFile("/data/examples/worked-example.gpxil.tif") as g:
picture = render(g["chrT"], mode="haplotypes", bases_per_row=4, mark_insertions=True)
print(picture.shape) # 6 rows of pixels: 2 for each of the 3 rows of bases
print(picture.index) # palette indices
picture.scaled(16).save_png("/home/user/results/worked-example-haplotypes.png")
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome pixel images (GPxI)
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- The Hilbert-curve layout
- Anders S (2009) Visualization of genomic data with the Hilbert curve. Bioinformatics 25(10): 1231–1235. doi:10.1093/bioinformatics/btp152
- The Okabe–Ito colours
- Okabe M, Ito K (2002) Color Universal Design (CUD): how to make figures and presentations that are friendly to colorblind people. jfly.uni-koeln.de/color
- Pillow, which writes the PNG pictures
- Jeffrey A. Clark and contributors. Pillow, the friendly PIL fork. python-pillow.github.io
Print it as a book (PDF)
gpxil book lays a gpxil file out as a PDF to print, one dot a base, page after page: a cover with a key to the colours, a page on how to read it, then each contig with a title page of its own and pages of bases, each headed with its first and last position.
The form
- The gpxil file
- Any gpxil file; one written without its reference needs Its reference too, as for a picture.
- Only these contigs
- Contigs separated by commas, such as
chr17; by default, all of them.
- Show
- The four views of Draw a region as a picture: genotype by default for a file with a sample, else the reference.
- Colours
- A palette. process prints each base in one pure ink.
- For
- print: colours given in CMYK, in the palette’s ink values; the screen: RGB.
- Paper
- A4 (the default), A3, A5 or US letter.
- Dots per inch
- The printer’s resolution: 300 by default, one base a dot. A page of A4 at 300 dots per inch holds 2,126 bases a row and 2,970 rows: 6,314,220 bases.
- Dots per base
- Each base as a square of this many dots a side, to make them bigger.
- Margin
- In millimetres, 15 by default.
- Title
- For the cover; by default “The genome of HG002”, with the sample’s name, or “The GRCh38 genome”, with the reference’s.
- At most this many pages
- A proof of the first pages, counting the cover, the page on how to read the book and each contig’s title page: 5 pages give two pages of bases. Clear it for the whole book.
- Save as
- The PDF, in
~/results.
What it writes
A PDF, and how many pages it has. Each page of bases is one image with its colour table in CMYK (or RGB), with smoothing turned off, so each base stays a crisp square dot. HG002’s chromosome 17 makes 17 pages and about 22 MB; the 25 chromosomes of GRCh38 at 300 dots per inch, about 530 pages, a title page for each chromosome included.
Printed at one dot a base, a person’s genome can be scanned and read back. Treat a book of a person’s genotype or variants as you would their VCF.
In the terminal
gpxil book /data/grch38/chrM.gpxil.tif -o ~/results/chrM_book.pdf --mode reference
gpxil book /data/examples/HG002_chr17.gpxil.tif -o ~/results/HG002_chr17_proof.pdf --max-pages 5 --page A3 --dpi 600
| Option | |
FILE -o BOOK.pdf | the file, and the PDF to write |
--contigs A,B, --mode NAME, --palette NAME | which contigs, which view (genotype if you don’t say), which colours |
--rgb | RGB for the screen, instead of CMYK |
--page A4|A3|A5|letter, --dpi N, --scale N, --margin MM | the paper, the resolution, dots per base, the margin |
--title TEXT, --max-pages N | the title; a proof of the first pages |
--reference FILE | the reference, for a file written without one |
In Python
from gpxiltools import GpxilFile
from gpxiltools.book import BookLayout, estimate_pages, make_book
layout = BookLayout(page="A4", dpi=300)
print(layout.grid(), f"{layout.bases_per_page():,} bases a page")
print(estimate_pages(3_088_286_401, 25, layout), "pages for GRCh38’s 25 chromosomes")
with GpxilFile("/data/grch38/chrM.gpxil.tif") as g:
print(make_book(g, "/home/user/results/chrM.pdf", mode="reference", layout=layout))
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
The palettes
A gpxil file holds codes, not colours. A palette turns them into colours when a picture is drawn: into screen colours (sRGB) for a PNG, or directly into ink (CMYK percentages) for print, so neither is converted from the other. gpxil palettes lists them.
classic- The colours of the original gpxil notebooks, and the default.
- A red C blue G green T yellow N near-black
okabe-ito- Colours that people with any common colour-blindness can tell apart.
- A bluish green C blue G orange T vermilion N near-black
gpxi- The grey levels of Albrecht-Buehler’s genome pixel images, after which gpxil is named.
- A black C dark grey G white T light grey N pale blue
process- One pure printing ink for each base, which prints most faithfully.
- A cyan C magenta G yellow T black N grey (35 % black)
Every palette also has a colour for “no base” (paper), and one for N (an unknown base) that no base or mix of bases has: near-black in classic and okabe-ito, whose mixes include greys, so that a heterozygous base never looks unknown. Neither is paler in a soft-masked repeat. The other codes are worked out from the four bases: an ambiguous base, or a heterozygous one in the genotype view, is the mean of its bases’ colours (in the process palette, the inks overprint), and a base in a soft-masked repeat is faded 45 % towards white (not in gpxi, whose greys need all their levels).
With the process palette, the code of a base is its ink: the four bits of a ref code, A, C, G and T, lay down cyan, magenta, yellow and black. A heterozygous A/G base prints cyan and yellow, green.
In the terminal
gpxil palettes
In Python
from gpxiltools.palettes import describe
for p in describe():
print(p["name"], p["srgb"]["A"], p["cmyk_percent"]["A"], "–", p["description"])
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome pixel images (GPxI)
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- The Okabe–Ito colours
- Okabe M, Ito K (2002) Color Universal Design (CUD): how to make figures and presentations that are friendly to colorblind people. jfly.uni-koeln.de/color
Sequences as FASTA
gpxil fasta writes a sequence from a gpxil file as FASTA: the reference’s, either of the person’s two haplotypes, or their genotype as one sequence of IUPAC codes, for a region or for every contig.
The form
- The gpxil file
- Any gpxil file; one written without its reference needs Its reference too.
- Sequence
- the reference; haplotype 1 or haplotype 2: the reference with that haplotype’s variants applied – substitutions, deletions and insertions – so it can be longer or shorter than the region, with N where the haplotype wasn’t called; or the genotype, in IUPAC codes: as long as the reference, with each base where the haplotypes differ as the code of both (R for A or G, Y for C or T …). A file without a sample has only the reference.
- Region
- A contig, or
CONTIG:START-END counting from 1. Empty: every contig, whole. In the browser, at most 50 million bases go into one file.
- Save as
- The FASTA file, named after the file, the region and the sequence, in
~/results.
What it writes
A FASTA file with a record for each contig or region, 60 bases a line. Its name says which sequence it is: >chr17:7661779-7687546_hap1, say, or just >chr17 for the whole of a contig’s reference. Lower case, for the reference’s soft-masked repeats, is kept.
In the terminal
gpxil fasta /data/examples/worked-example.gpxil.tif
gpxil fasta /data/examples/worked-example.gpxil.tif --which 1
gpxil fasta /data/examples/worked-example.gpxil.tif --which iupac
gpxil fasta /data/examples/HG002_chr17.gpxil.tif --which 2 --region chr17:7676101-7676200 -o ~/results/hap2_P72R.fa
| Option | |
--which ref|1|2|iupac | the sequence (the reference if you don’t say) |
--region REGION | one region; without it, every contig |
-o FILE | the FASTA file; without it, the sequence is printed |
--reference FILE | the reference, for a file written without one |
In Python
from gpxiltools import GpxilFile
from gpxiltools.export import genotype_iupac_sequence, haplotype_sequence
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
c = g["chr17"]
print(c.sequence(7_676_100, 7_676_200)) # the reference
print(haplotype_sequence(c, 1, 7_676_100, 7_676_200).decode()) # haplotype 1
print(genotype_iupac_sequence(c, 7_676_100, 7_676_200).decode())
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- IUPAC nucleotide codes
- Cornish-Bowden A (1985) Nomenclature for incompletely specified bases in nucleic acid sequences: recommendations 1984. Nucleic Acids Research 13(9): 3021–3030. doi:10.1093/nar/13.9.3021
Variants as VCF
gpxil vcf writes the person in a gpxil file as a VCF again, so that any other genomics program can read their variants: a record for each place where either haplotype differs from the reference, and, if you like, a block for each stretch that wasn’t called.
The form
- The gpxil file
- A gpxil file with a sample; one written without its reference needs Its reference too, to spell out the alleles.
- Regions
- One or more regions, separated by spaces: a contig, or
CONTIG:START-END counting from 1. Empty: every contig. A window’s records are its chromosome’s, at the chromosome’s positions; windows that overlap or touch are read as one stretch, so each record comes once, whole. At a window’s ends a record says only what the window holds.
- Write no-call blocks
- On by default: each stretch of bases that weren’t called becomes a record with ALT
<*>, an END and genotype ./., so that a program can tell “not called” from “the same as the reference”, and encoding the VCF again restores them.
- Save as
- The VCF file, in
~/results.
What it writes
A VCF (version 4.3) with one sample, named as in the gpxil file. Each record covers a stretch of the reference where either haplotype differs from it: REF is the reference over the stretch, and each haplotype’s sequence over it is an allele, so neighbouring changes may share a record. The genotype is phased (0|1) only where every called base of the record was phased. GQ, DP and PS are given where the file has those planes; a record over bases that failed their filters has FILTER gpxil_filtered; a complex variant keeps what the original VCF said of it in GPXIL_COMPLEX.
The VCF of a region has exactly the records that the whole contig’s VCF has there, so a deletion that starts just before the region is written whole; no-call blocks are cut at the region’s ends. IDs, QUAL, INFO and the other FORMAT fields of the original VCF aren’t in a gpxil file, so they aren’t written: the records are equivalent to the originals, not copies of them.
In the terminal
gpxil vcf /data/examples/worked-example.gpxil.tif
gpxil vcf /data/examples/HG002_chr17.gpxil.tif --region chr17:7661779-7687546 --region chr17:43044292-43170245 -o ~/results/TP53_BRCA1.vcf
gpxil vcf /data/examples/HG002_chr17.gpxil.tif --region chr17:7661779-7687546 --no-nocall-blocks -o ~/results/TP53_variants_only.vcf
| Option | |
--region REGION | a region; give it again for more |
--contigs A,B | whole contigs (not with --region) |
--no-nocall-blocks | leave out the no-call blocks |
-o FILE | the VCF; without it, the VCF is printed |
--reference FILE | the reference, for a file written without one |
In Python
from gpxiltools import GpxilFile
from gpxiltools.export import iter_vcf_records
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
for line in iter_vcf_records(g["chr17"], start=7_676_100, end=7_676_200, nocall_blocks=False):
print(line)
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- VCF, the variant call format
- Danecek P, Auton A, Abecasis G, Albers CA, Banks E, DePristo MA, et al. (2011) The variant call format and VCFtools. Bioinformatics 27(15): 2156–2158. doi:10.1093/bioinformatics/btr330
- GA4GH and the SAMtools team (hts-specs). The Variant Call Format specification, VCFv4.4. samtools.github.io/hts-specs/VCFv4.4.pdf
Genes, transcripts and proteins as pxil pictures
gpxil2pxil spells out a gene from a gpxil file – from its reference, or from either of the person’s haplotypes – splices its transcripts and translates their proteins, and writes each as a pxil file: a PNG picture of the sequence, one pixel a residue, which holds the sequence exactly. A picture made from a haplotype says how it differs from the reference’s.
The form
- The gpxil file
- A gpxil file on the assembly of the genes (GRCh38 for the default genes): a person’s, or a chromosome of the reference from
/data/grch38. A file of windows will do, such as /data/examples/HG002_TP53.gpxil.tif: each gene is found in the window that holds it, and gives the same pictures as from the whole chromosome.
- Genes
- Symbols (
TP53) or Ensembl gene IDs (ENSG00000141510), separated by spaces. With the Gpxil browser’s genes, an alias that names only one gene will do too.
- Transcripts
- the canonical transcript, Ensembl’s choice of one for each gene (the default); the MANE transcripts, MANE Select and MANE Plus Clinical, which Ensembl and NCBI agree on; every protein-coding transcript; or every transcript. In the terminal, you can also name transcripts:
-t TP53-201,ENST00000420246.
- Pictures of
- The gene (its genomic sequence, introns included), the transcripts (spliced) and the proteins; or some of these.
- From
- both haplotypes for a file with a sample, else the reference (the default); the reference and both haplotypes; or one of them.
- Genes from
- By default, the Gpxil browser’s gene files: every transcript of Ensembl release 116 on GRCh38, read from the web a gene at a time. Or a GTF file (Ensembl or GENCODE), for another annotation or another assembly, or to work without the web:
/data/examples/TP53.gtf has TP53’s lines.
- Residues per row
- By default, about square: in steps of 10 for a protein, 30 for a transcript (whole codons) and 100 for a gene.
- Its reference
- Only for a file written without its reference plane.
- Only list each gene’s transcripts
- No pictures: each transcript’s name, ID, biotype, protein and tags, with a star by the canonical one.
- Save in
- The folder for the pictures,
~/results/pxil/gene by default.
What it writes
For each transcript and each source, the pictures you asked for, named with their Ensembl IDs, and with the sample and haplotype when they come from one:
| File | What it holds |
TP53_ENSG00000141510.gene.pxil.png | the gene’s genomic sequence, 5′ to 3′, introns and soft-masked repeats included |
TP53-201_ENST00000269305.transcript.pxil.png | the spliced transcript, 5′ to 3′ |
TP53-201_ENSP00000269305.protein.pxil.png | the protein, from the start codon to the stop codon (*) |
TP53-201_ENSP00000269305_HG002-hap1.protein.pxil.png | the same protein, made from HG002’s first haplotype |
It prints a line for each file, with what changed: 39 changes from the reference for a gene, 2 changes from the reference in its exons for a transcript, differs from the reference: P72R for a protein (or the same as the reference). The same notes are in each picture’s metadata, with its coordinates, exons, coding region, genetic code and source. The walkthrough says how the sequences are made, and pxil pictures of sequences how the pictures hold them.
In the terminal
gpxil2pxil /data/examples/HG002_chr17.gpxil.tif TP53 --annotation /data/examples/TP53.gtf --kinds protein --haplotypes all -o ~/results/pxil/TP53
gpxil2pxil /data/examples/HG002_chr17.gpxil.tif TP53 BRCA1 --kinds protein -o ~/results/pxil/TP53_BRCA1
| Option | |
FILE GENE … | the gpxil file (a path, or the address of one on the web), and genes |
-t canonical|mane|coding|all|NAMES | which transcripts |
-k gene,transcript,protein | which pictures |
--haplotypes ref,1,2,both,all,auto | from whom |
-a GTF|FOLDER|URL | the genes, if not the Gpxil browser’s |
-o FOLDER, --width N | where to write; residues per row |
--reference FILE, --list | the reference for a file written without one; list the transcripts only |
In Python
from gpxiltools import GpxilFile
from gpxil2pxil import GTF, Source, make, translate
gene = GTF("/data/examples/TP53.gtf").find("TP53")
for t in gene.select("mane"):
print(t.name, t.id, t.protein, t.tags())
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
spliced = Source(g, 2).spliced(gene.canonical()) # haplotype 2's transcript
print(len(spliced.letters()), "bases; coding from", spliced.cds_start)
files = make(g, gene, kinds=["transcript"], haplotypes=(None, 2), out_dir="/home/user/results/pxil/TP53-transcripts")
print([f.notes for f in files])
print(translate("ATGGAGGAGCCGCAGTCAGATCCTAGCTAA"))
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- Ensembl (GRCh38 sequence, and genes from release 116)
- Yates AD, Austine-Orimoloye O, Azov AG, Barba M, Barnes I, Barrera-Enriquez VP, et al. (2026) Ensembl 2026. Nucleic Acids Research 54(D1): D1053–D1060. doi:10.1093/nar/gkaf1239
- MANE transcripts
- Morales J, Pujar S, Loveland JE, Astashyn A, Bennett R, Berry A, et al. (2022) A joint NCBI and EMBL-EBI transcript set for clinical genomics and research. Nature 604(7905): 310–315. doi:10.1038/s41586-022-04558-8
pxil pictures → FASTA
pxil reads the sequences back out of pxil pictures and writes them as FASTA, or writes what each picture says about itself as JSON. Because a pixel’s value is a residue’s place in the picture’s alphabet, the sequence comes back exactly, whatever the colours.
The form
- The pxil files
- One or more pxil pictures: choose them one at a time from the list and press Add. The pictures that Genes, transcripts and proteins as pxil pictures just wrote can go straight in, from the button under its result.
- Their metadata (JSON) instead of their sequences
- Each picture’s metadata: what kind of sequence it is, its IDs and name, its alphabet and length, and for a gene’s pictures its contig, coordinates, exons, coding region, genetic code, source, and the sample, haplotype and notes of one made from a person.
- Save as
- The FASTA (or JSON) file, in
~/results.
What it writes
A FASTA record for each picture, named with its ID, name and kind, such as >ENSP00000269305 TP53-201 protein, 60 residues a line. A picture of a person’s own version of a sequence has the person and the haplotype joined to its ID – >ENSP00000269305_HG002-hap1 TP53-201 protein – so the reference’s protein and each haplotype’s keep names of their own in one file, as an alignment program needs.
In the terminal
gpxil2pxil /data/examples/HG002_chr17.gpxil.tif TP53 --annotation /data/examples/TP53.gtf --kinds protein --haplotypes all -o ~/results/pxil/TP53
pxil ~/results/pxil/TP53/*.protein.pxil.png -o ~/results/TP53_proteins.fa
pxil --info ~/results/pxil/TP53/TP53-201_ENSP00000269305_HG002-hap2.protein.pxil.png
| Option | |
FILE … | one or more pxil files |
--info | the metadata, as JSON, instead of the sequences |
--line N | residues per FASTA line (60) |
-o FILE | write to a file; without it, print |
On your own computer, python -m pxil does the same.
In Python
import pxil
sequence, meta = pxil.read("/home/user/results/pxil/TP53/TP53-201_ENSP00000269305_HG002-hap2.protein.pxil.png")
print(meta["name"], meta["kind"], meta["length"], meta.get("notes"))
print(sequence[60:80])
pxil.write("/home/user/results/my.transcript.pxil.png", "ATGGAGGAGCCGCAGTCAGATCC", kind="transcript", id="my-1")
print(pxil.read("/home/user/results/my.transcript.pxil.png")[0])
This Python box reads the pictures the terminal box above wrote: run that first.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
The formats
The studio works with a handful of kinds of file. It tells them apart by their names, so keep the usual endings. gpxil is in the middle: a reference and a person’s variants go in, and pictures, books, gene and protein pictures, and FASTA and VCF again come out.
| Kind | Names | What it holds | Read by | Written by |
| gpxil | .gpxil.tif | a genome as pictures: the reference and one person’s genotype, base by base (The gpxil format) | every gpxil tool, the Viewer | encode |
| FASTA | .fa, .fasta, .fna, .fas (also .gz) | sequences: a reference genome, a haplotype, a protein | encode (as the reference) | gpxil fasta, pxil |
| VCF | .vcf, .vcf.gz | a person’s variants, as differences from the reference (How a VCF becomes gpxil) | encode | gpxil vcf |
| BED | .bed | regions of the genome: here, those called with confidence | encode | – |
| GTF | .gtf (also .gtf.gz), from Ensembl or GENCODE | genes, transcripts, exons and coding regions | gpxil2pxil | – |
| pxil | .pxil.png, such as .protein.pxil.png | one sequence as a picture, one pixel a residue (pxil pictures of sequences) | pxil | gpxil2pxil |
| picture | .png, .tif | a region drawn in colour, for the screen or for print | (any image software) | gpxil render, the Viewer |
| PDF | .pdf | a book to print, one dot a base | (any PDF reader) | gpxil book |
Besides these, the studio has notebooks (.ipynb), workflows (.workflow.json) and the records of their runs (workflow-run.json), and opens other text files, such as scripts, in the Files tab’s editor.
Why gpxil is a picture
A genome is a sequence of letters, and a sequence of letters can be laid out in rows, like text, with one pixel for each letter. Guenter Albrecht-Buehler drew genomes this way, in shades of grey, and found that repeats line up into patterns you can see. gpxil keeps the one-pixel-per-base picture but stores codes rather than colours, so that it holds the sequence exactly and a person’s genotype beside it, in ordinary TIFF files that any image software can open. pxil does the same for a single sequence, in a PNG.
- The gpxil formatHow a gpxil file holds a genome: contigs and planes, windows, layout, pages, strips, the sidecar, the metadata and the checksums
- The codesEvery value of the ref, gt and flags planes, and the specification’s worked example, pixel by pixel
- How a VCF becomes gpxil, and backHow each kind of call is written base by base, which call wins when they overlap, what is kept and what isn’t, and how a gpxil file becomes a VCF again
- pxil pictures of sequencesThe pxil format: one sequence as a PNG picture, one pixel a residue, with what it is in its metadata
- Big files and memoryHow the studio works with files of hundreds of megabytes in a browser tab, its limits, and how to write code that stays small
- The GRCh38 reference/data/grch38: GRCh38 as a gpxil file for each chromosome, read from the Gpxil browser; its genes; and the whole genome in one file to download
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- Genome pixel images (GPxI)
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- VCF, the variant call format
- Danecek P, Auton A, Abecasis G, Albers CA, Banks E, DePristo MA, et al. (2011) The variant call format and VCFtools. Bioinformatics 27(15): 2156–2158. doi:10.1093/bioinformatics/btr330
- GA4GH and the SAMtools team (hts-specs). The Variant Call Format specification, VCFv4.4. samtools.github.io/hts-specs/VCFv4.4.pdf
The gpxil format
A gpxil file stores a genome as pictures: one pixel a base, one picture for each chromosome and each kind of information, in an ordinary multi-page TIFF file. Any image software opens it as a picture of the genome; image libraries read its pictures as arrays of numbers; and it holds the reference and one person’s genotypes exactly, so they can be got back base for base. This page describes the format; the specification, version 0.3 (a draft), gives every rule.
Contigs and planes
A gpxil file has a list of contigs – chromosomes, the mitochondrion, unplaced scaffolds – and each contig has one or more planes: arrays with one value for each of its bases. Every plane is lined up with the reference, so the same pixel is the same position in every plane, and in every person’s file.
| Plane | Type | What it holds |
ref | 8-bit | the reference base, as an IUPAC code, and whether it is soft-masked (lower case: in a repeat) |
gt | 8-bit | the person’s genotype: what each of their two haplotypes has at this base, four bits each |
flags | 8-bit | whether the call here was phased, an insertion follows, the call was filtered, a call covers the base |
gq, dp | 8-bit | the genotype quality and read depth of the call over the base, up to 254; 255 where there is none |
ps | 32-bit | the phase set of a phased call; 0 where there is none |
x-… | 8, 16 or 32 bits, or floating point | planes of your own, such as x-methylation; readers ignore those they don’t know. gpxil annotate makes them from repeats, BED and bedGraph files, with what they hold in the metadata |
A file holds at most one person. One without a person is a reference – ref planes only, like the chromosomes of GRCh38 in /data/grch38. A file can also leave the ref plane out and name its reference by checksums instead (below). A contig with a gt plane always has a flags plane too. The codes gives every value.
Windows: part of a chromosome
A contig can also be a window (new in version 0.3): it holds a stretch of a longer sequence, such as TP53 and 100,000 bases either side out of chromosome 17, and its entry says where the stretch lies:
{"name": "chr17:7561779-7787546", "length": 225768, "pages": {"ref": 0, "gt": 1, …},
"md5": "…", "refget": "…",
"window": {"of": "chr17", "start": 7561778, "length": 83257441, "md5": "…", "refget": "…", "aliases": ["17"]}}
of names the chromosome, start is the position of the window’s first base on it (counting from 0), and the chromosome’s length and checksums say which sequence it is. The window’s own md5 and refget are those of its own bases. Its planes hold exactly what a contig of the whole chromosome would hold over those bases, so cutting a window out of a file, or encoding just its region from a VCF, gives the same window. Readers give positions as the chromosome’s: base i of the window is position start + i on it, and a region of the chromosome is found in the window that holds it. A file can hold several windows of a chromosome, even overlapping ones, and the whole chromosome too. A window is named for the stretch it holds, from 1 and including both ends, so a window’s name is also the region it holds.
Layout: bases in rows
Each plane is a picture W pixels wide, filled like a page of text: base i, counting from 0, is in row i ÷ W and column i mod W, and the end of the last row is padded with zeros. The writer chooses the width and records it for each contig. gpxiltools uses 8,192, so chromosome 17’s 83,257,441 bases make pictures of 10,164 rows.
A plane is really a line of values, so any program can re-wrap it to another width without losing anything. A width equal to a repeat’s period lines the repeat up in columns. Other views, such as a book’s pages or a Hilbert curve, are made from the rows when they are drawn.
Pages: one picture for each plane
Every plane of every contig is one page of the TIFF file. The pages go contig by contig, in the order ref, gt, flags, gq, dp, ps, then planes of your own. Each page describes itself in JSON, such as {"gpxil": "0.3", "contig": "chr17", "plane": "gt", "length": 83257441}, and has a name for image viewers, such as chr17 gt. The first page also carries the metadata of the whole file.
The ref, gt and flags pages are palette pictures with a colour map, so that an ordinary image viewer shows them in colour. The colours are only for looking: no reader takes values from them. The other pages are greyscale.
Strips: reading a region
Each page is cut into strips of whole rows, and each strip is compressed on its own with Deflate (zlib), which every TIFF reader understands. gpxiltools makes strips of 256 KB before compression: 32 rows of 8,192 bases – 262,144 bases – for an 8-bit plane, and 8 rows for the 32-bit ps. To read a region, a reader works out the rows it covers and decompresses only their strips. So a gene costs the same to read from a file of one chromosome as from a file of the whole genome, and over the web only those strips need fetching (Big files and memory).
The sidecar: what doesn’t fit in a pixel
An insertion happens between two bases, so it has no pixel of its own. The flags plane marks the base it follows (INS1 or INS2, by haplotype), and the inserted bases go in the contig’s sidecar: a small table, compressed, in a private tag of the gt page (tag 65400). The sidecar also describes complex variants, such as inversions, which can’t be written base by base. This is the sidecar of the specification’s worked example – two Gs inserted after offset 7, on both haplotypes:
#gpxil-sidecar 0.1
offset hap kind data
7 1 INS GG
7 2 INS GG
Programs built on libtiff, such as ImageMagick, warn about the unknown tag and otherwise ignore it.
The metadata
The first page’s description is a JSON object that says what the file holds. It must have gpxil (the version of the specification whose rules made it, "0.3"; files made by gpxiltools 0.6 say "0.2", and by gpxiltools 0.5 or earlier "0.1", and read the same; a file with windows says "0.3" or later), layout (the default width), contigs and, in a file with a person, sample. Usually there too are reference (its name and source file), planes (their types and missing values), palette, generator, created and summary, the encoder’s totals. Each contig’s entry gives:
| Key | What it holds |
name, length, width | the contig’s name, its number of bases, and its layout width |
pages | which page holds each plane: {"ref": 0, "gt": 1, "flags": 2, …} |
md5, refget | the checksums of its reference sequence (below) |
aliases | other names for it, such as 17 for chr17 |
window | for a window, where it lies (above) |
ploidy | 1 if haplotype 2 is ABSENT everywhere, 2 if nowhere, otherwise "mixed" |
encoding, callable_bases, sidecar_entries, phase_sets | what the encoder did (records applied, conflicts, mismatches with the reference …), the bases in the callable regions, the sidecar’s entries, and the phase sets |
The sample object names the person (id, as in the VCF) and records what was applied: the VCF, the callable regions, the haploid regions and the sex. Readers ignore keys they don’t know, so writers can add their own.
Checksums: naming the reference
Each contig’s reference sequence has two checksums: its MD5, as in a SAM or CRAM header (@SQ M5), and its GA4GH refget identifier (SQ. and 32 characters). Both are worked out from the sequence in capital letters. They let a file written without its ref plane say exactly which reference it needs, and they let a reader check that a reference it is given is that one: gpxiltools refuses a reference whose checksums don’t match. For a FASTA file of plain IUPAC letters, they are the checksums other tools work out.
Why TIFF
- Every image program opens it, so a gpxil file is a picture of the genome before it is anything else, and can be printed as one.
- Image libraries – tifffile, Pillow, and those of image analysis – read its pages as arrays, so image operations become genome operations: the difference of two people’s
gt planes, say, or a box filter over the G and C bits of ref for the GC content.
- Strips give random access, and standard compression keeps files small: HG002’s chromosome 17, with all six planes, is 24.6 MB, and the whole of GRCh38 is 848.5 MB.
- A file over 4 GB has to be a BigTIFF, which gpxiltools switches to by itself. A whole human genome with one person is under 1 GB.
Look for yourself
gpxil info /data/examples/HG002_chr17.gpxil.tif
gpxil info --json /data/examples/worked-example.gpxil.tif
In Python, tifffile reads a gpxil file as the ordinary TIFF it is, and Pillow sees a palette picture of six frames:
import json
import tifffile
from PIL import Image
with tifffile.TiffFile("/data/examples/HG002_chr17.gpxil.tif") as tif:
meta = json.loads(tif.pages[0].description)
print(sorted(meta))
for page in tif.pages:
print(page.index, page.tags["PageName"].value, page.shape, page.dtype,
f"{len(page.dataoffsets)} strips of {page.rowsperstrip} rows", "sidecar" if 65400 in page.tags else "")
with Image.open("/data/examples/HG002_chr17.gpxil.tif") as im:
print(im.mode, im.size, im.n_frames)
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Genome pixel images (GPxI)
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- tifffile, which gpxiltools reads and writes TIFF with
- Gohlke C. tifffile: read and write TIFF files. github.com/cgohlke/tifffile
- Pillow, which writes the PNG pictures
- Jeffrey A. Clark and contributors. Pillow, the friendly PIL fork. python-pillow.github.io
- refget sequence identifiers
- Yates AD, Adams J, Chaturvedi S, Davies RM, Laird M, Leinonen R, et al. (2021) Refget: standardized access to reference sequences. Bioinformatics 38(1): 299–300. doi:10.1093/bioinformatics/btab524
The codes
A gpxil file holds numbers, not colours. This page gives the meaning of every number in the three main planes – the reference (ref), the genotype (gt) and the flags – and then works through the specification’s example, pixel by pixel.
ref: the reference base
One byte a base. Its low four bits are the base as a set of A, C, G and T, one bit each: A 1, C 2, G 4, T 8. So an ambiguity code is the sum of its bases – R, A or G, is 1 + 4 = 5 – and N, any base, is 15. These are the codes BAM files use for bases. 0 means no base (a gap). The next bit, 16, marks a soft-masked base: one written in lower case in the reference’s FASTA file, because it lies in a repeat.
| Code | Base | Stands for | | Code | Base | Stands for |
| 0 | - | no base | | 8 | T | T |
| 1 | A | A | | 9 | W | A or T |
| 2 | C | C | | 10 | Y | C or T |
| 3 | M | A or C | | 11 | H | A, C or T |
| 4 | G | G | | 12 | K | G or T |
| 5 | R | A or G | | 13 | D | A, G or T |
| 6 | S | C or G | | 14 | B | C, G or T |
| 7 | V | A, C or G | | 15 | N | any base |
Add 16 for a soft-masked base: a is 17, c 18, n 31. So a ref byte is 0 to 15, or 17 to 31.
gt: the genotype
One byte a base. Its high four bits are what haplotype 1 has there, its low four bits what haplotype 2 has: the byte is haplotype 1 × 16 + haplotype 2. In hexadecimal, that makes the first digit haplotype 1 and the second haplotype 2. Each haplotype’s state is relative to the reference base:
| State | Name | The haplotype… |
| 0 | REF | has the reference base |
| 1, 2, 4, 8 | A, C, G, T | has this base instead (the same bits as in ref) |
| 10 (hex A) | ABSENT | doesn’t exist here: the person has one copy, as a man has of most of X and Y |
| 12 (hex C) | COMPLEX | has something that can’t be written base by base, described in the sidecar |
| 13 (hex D) | DEL | has lost this base: it is deleted |
| 15 (hex F) | UNKNOWN | wasn’t called, so isn’t known |
The other values are reserved, and readers treat them as UNKNOWN. The special states’ letters are reminders: Absent, Complex, Deleted, and F – all four bits – for unknown, as N is all four bits in ref. A haplotype that has the reference base is always REF, never the base itself, so a base state always means a change.
| gt | Hex | Haplotype 1 | 2 | Meaning |
| 0 | 00 | REF | REF | both copies as the reference |
| 4 | 04 | REF | G | heterozygous: G on haplotype 2 |
| 68 | 44 | G | G | homozygous G |
| 72 | 48 | G | T | heterozygous, with a different change on each |
| 13 | 0D | REF | DEL | deleted on haplotype 2 |
| 221 | DD | DEL | DEL | deleted on both |
| 26 | 1A | A | ABSENT | one copy, which has A |
| 255 | FF | UNKNOWN | UNKNOWN | not called |
Insertions aren’t states: they happen between bases. The base an insertion follows is flagged (below), and the inserted bases are in the sidecar (The gpxil format).
flags
One byte a base. Each bit is a flag, and a base can have several:
| Bit | Name | Meaning |
| 1 | PHASED | the call over this base was phased (0|1), so haplotypes 1 and 2 are the two parental copies, not just two alleles |
| 2 | INS1 | an insertion follows this base on haplotype 1 |
| 4 | INS2 | an insertion follows this base on haplotype 2 |
| 8 | FILTERED | a call over this base didn’t pass its filters: its FILTER was neither PASS nor . |
| 16 | VARIANT_SITE | a call covers this base, whatever its genotype |
VARIANT_SITE tells “the VCF said this base is the reference” (REF, with the flag) from “the reference, because the VCF said nothing” (REF, without it). Bits 32, 64 and 128 are reserved, and always 0.
The other planes
gq and dp hold the genotype quality and read depth of the call over each base, from 0 to 254 (bigger values are stored as 254), and 255 where no call with that field covers the base. ps holds the phase set of the phased call over each base, as a 32-bit number, and 0 where the base isn’t phased.
The worked example, pixel by pixel
The specification’s example has a reference of ten bases, ACGTACGTac – the last two soft-masked – and four calls:
#CHROM POS REF ALT FILTER GT
chrT 2 C T PASS 0|1
chrT 4 TAC T PASS 1|0
chrT 8 T TGG PASS 1|1
chrT 9 A G LowQ ./1
| Offset | Base | ref | gt | Haplotype 1 | 2 | flags |
| 0 | A | 1 | 0 | REF | REF | 0 |
| 1 | C | 2 | 8 | REF | T | 17: PHASED, VARIANT_SITE |
| 2 | G | 4 | 0 | REF | REF | 0 |
| 3 | T | 8 | 0 | REF | REF: the deletion’s anchor | 17 |
| 4 | A | 1 | 208 | DEL | REF | 17 |
| 5 | C | 2 | 208 | DEL | REF | 17 |
| 6 | G | 4 | 0 | REF | REF | 0 |
| 7 | T | 8 | 0 | REF | REF, with GG inserted after it on both | 23: PHASED, INS1, INS2, VARIANT_SITE |
| 8 | a | 17 | 244 | UNKNOWN | G | 24: FILTERED, VARIANT_SITE |
| 9 | c | 18 | 0 | REF | REF | 0 |
Spell each haplotype out base by base – REF gives the reference’s base, a change its own base, DEL nothing and UNKNOWN N, with each insertion after its base – and haplotype 1 is ACGTGTGGnc, haplotype 2 ATGTACGTGGgc, keeping the reference’s lower case. The genotype as one sequence of IUPAC codes, the bases of both haplotypes together, is AYGTACGTNc: Y, C or T, at offset 1, and N at offset 8, where haplotype 1 isn’t known. With 4 bases a row, each plane is a picture of 3 rows of 4 pixels, the last two of them padding.
In Python
from gpxiltools import GpxilFile
from gpxiltools import codes as C
with GpxilFile("/data/examples/worked-example.gpxil.tif") as g:
c = g["chrT"]
ref, gt, flags = c.read("ref"), c.read("gt"), c.read("flags")
print(c.sequence(), ref.tolist(), gt.tolist(), flags.tolist())
for i in range(c.length):
names = [name for bit, name in C.FLAG_NAMES.items() if flags[i] & bit]
print(i, c.sequence(i, i + 1), C.describe_gt_byte(int(gt[i])), names)
print(C.IUPAC[C.genotype_iupac(ref, gt)[1]]) # C on haplotype 1, T on haplotype 2: Y
print(c.image("ref")) # as stored: 3 rows of 4, with the padding
print(c.sidecar())
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- IUPAC nucleotide codes
- Cornish-Bowden A (1985) Nomenclature for incompletely specified bases in nucleic acid sequences: recommendations 1984. Nucleic Acids Research 13(9): 3021–3030. doi:10.1093/nar/13.9.3021
- VCF, the variant call format
- Danecek P, Auton A, Abecasis G, Albers CA, Banks E, DePristo MA, et al. (2011) The variant call format and VCFtools. Bioinformatics 27(15): 2156–2158. doi:10.1093/bioinformatics/btr330
- GA4GH and the SAMtools team (hts-specs). The Variant Call Format specification, VCFv4.4. samtools.github.io/hts-specs/VCFv4.4.pdf
How a VCF becomes gpxil, and back
A VCF lists a person’s variants as records; a gpxil file holds their genotype at every base. The specification fixes exactly how one becomes the other, so that any two encoders make the same planes from the same VCF. This page explains the rules, and why they are as they are. The codes gives the values they write.
1 · Before any call: what is assumed
- Every base of both haplotypes starts as REF, the reference.
- Given a BED file of callable regions – the regions called with confidence – both haplotypes are UNKNOWN outside them, and on a contig the BED file doesn’t mention, everywhere.
- Given haploid regions (
--haploid, --haploid-contig, or --sex XY for X and Y outside the pseudo-autosomal regions of GRCh38), haplotype 2 is ABSENT inside them.
A VCF of variant sites says nothing about the bases it doesn’t list, so these are assumptions, and the metadata records which were made. They are also weak: any call overrides them. A contig on which the VCF has no records, and which no callable region touches, gets no gt plane at all, because saying that the person has the reference all along a chromosome nobody looked at would be wrong.
2 · A record becomes states, base by base
A record’s REF allele covers its span: from POS, for as many bases as REF has. Each haplotype takes the allele its genotype names, and that allele becomes a state at each base of the span:
0, the REF allele: REF over the span. ., a missing allele: UNKNOWN.
- A sequence allele: REF and ALT are trimmed of the bases they share, first at the end and then at the start, always keeping an indel’s anchor base. What is left lines up base by base. Where it differs, the haplotype has the ALT base: a substitution. Bases of REF left over are DEL. Bases of ALT left over are an insertion after the base before them: flagged (INS1, INS2) and kept in the sidecar.
*, a base deleted by another record: nothing is written, because the record that starts the deletion writes it.
Trimming the end first puts an indel as far left as the record allows, as VCF normalisation does, so calls inside repeats agree with their neighbours. In the worked example, chrT 4 TAC T 1|0 is trimmed to the anchor T and the deleted AC, so offsets 4 and 5 are DEL on haplotype 1; and chrT 8 T TGG 1|1 leaves GG inserted after offset 7 on both haplotypes.
3 · Symbolic alleles and blocks
<DEL> (and <DEL:ME> …): the bases after POS, up to END – from INFO/END, or else INFO/SVLEN – are DEL. POS is the padding base before them, so it stays REF.
<NON_REF>, <*> and <X> mean “any other allele”. A record with only these, as in a gVCF, is a reference block: its 0 alleles say REF up to END. A haplotype whose allele is one of them is UNKNOWN.
- Anything else symbolic – an inversion, a duplication, a breakend: the haplotype is COMPLEX at the record’s first base, and the sidecar keeps the allele (
REF=A;ALT=<INV>;END=…). What the haplotype has over the rest of the span can’t be written base by base, so those bases keep what they had.
4 · When calls overlap: which wins
Calls are applied in the order of the file, and each base remembers whether a call has spoken for it. A, C, G, T, DEL and COMPLEX are strong states: they say what the haplotype has. REF, UNKNOWN and ABSENT are weak.
- A strong state always replaces what is there. If an earlier call had put a different strong state there, that is a conflict: the later call wins, and the metadata counts it.
- A weak state is written only where no call has spoken yet.
So a record whose REF allele overlaps another record’s variant never undoes that variant, whatever the order of the records, and overlapping records that use * combine as they should. A multi-allelic site split into two records at one position (0/1 and 0/1) goes one record to each haplotype (section 5). Written phased (0|1 and 0|1), both go to haplotype 2 as written: two substitutions there conflict, and of two deletions from one base, the longer stays.
5 · Genotypes, phase and the other fields
- Haploid genotypes, such as
1: haplotype 2 is ABSENT over the span. Genotypes of more than two alleles are skipped, and counted.
- Phased genotypes (
0|1): PHASED is set, and haplotype 1 takes the first allele. In unphased ones (0/1), haplotype 1 also takes the first allele written, but the order means nothing – except that a * goes to the haplotype that has the deletion it stands for, and that an allele that doesn’t fit its haplotype goes to the other one, if both alleles fit that way round. Calls that overlap are alternatives at one place: an ALT allele occupies the bases it changes (and, for an insertion or a deletion, the base before it), and doesn’t fit a haplotype where an earlier call’s ALT allele occupies one of them. So two heterozygous deletions that overlap, the second starting at a base the first deletes, go one to each haplotype, and so do the records of a multi-allelic site split in two, in either order; the same record written twice stays on one. This is version 0.2 of the specification; version 0.1 put both on haplotype 2, as one longer deletion. The metadata counts them (overlapping_calls_reassigned).
- A missing genotype (
./., or no GT): both haplotypes UNKNOWN, but only where no call has spoken, so it never erases a variant.
- Flags add up over all the calls over a base: VARIANT_SITE over every call’s span, FILTERED where a call’s FILTER is neither PASS nor
..
- GQ, DP and PS are written over the call’s span, GQ and DP up to 254. Where calls overlap, one that changed the base wins over one that didn’t.
6 · Checks
Every REF allele is checked against the reference, and mismatches are counted (ref_mismatches), so a VCF called against another reference shows up at once. VCF contigs are matched to the reference’s with or without chr, and MT with chrM, and the VCF’s name is kept as an alias. Records past the end of a contig are skipped, and counted. The counts are in each contig’s encoding metadata, and the encoder reports anything worth knowing.
7 · What is kept, and what isn’t
Kept exactly: the reference and its soft-masking; both haplotypes’ sequences – every substitution, deletion and insertion; phasing; haploid calls; no-calls; whether each call passed its filters; and the genotype quality, depth and phase set at each base.
Not kept: the records themselves – how the alleles were written, and where one record ended and the next began – IDs, QUAL, INFO, the other FORMAT fields, other samples, records on contigs the reference doesn’t have, and the bases of structural variants other than deletions, which are kept only as their COMPLEX entries.
8 · And back: gpxil to VCF
gpxil vcf writes a record for each event window. A substitution is a window of one base; a run of deleted bases takes the base before it as its anchor; an insertion takes the base it follows; and windows that share a base are merged. REF is the reference over the window, and each haplotype’s sequence over it is an allele. The genotype is phased only if every called base of the window was. FILTER is PASS, or gpxil_filtered where the bases failed their filters, since the filter’s own name isn’t kept. Runs of UNKNOWN become no-call blocks (ALT <*>, with END, and genotype ./.), so that encoding the VCF again restores them.
The records are equivalent to the originals – they spell the same two haplotypes – not copies of them: two records next to each other may come back as one, and an indel in a repeat may come back at another, equally correct place. Encoding the exported VCF gives the same haplotypes again.
Try it: there and back
gpxil encode /data/examples/worked-example.fa -v /data/examples/worked-example.vcf --width 4 -o ~/results/chrT.gpxil.tif
gpxil vcf ~/results/chrT.gpxil.tif -o ~/results/chrT.vcf
grep -v '^##' ~/results/chrT.vcf
gpxil encode /data/examples/worked-example.fa -v ~/results/chrT.vcf --width 4 -o ~/results/chrT.again.gpxil.tif
gpxil fasta ~/results/chrT.gpxil.tif --which 1
gpxil fasta ~/results/chrT.again.gpxil.tif --which 1
And what encoding HG002’s chromosome 17 recorded:
from gpxiltools import GpxilFile
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
print(g.metadata["sample"])
e = g["chr17"].entry
print(f"{e['callable_bases']:,} bases called with confidence; {e['sidecar_entries']:,} sidecar entries; ploidy {e['ploidy']}")
print({k: v for k, v in e["encoding"].items() if v})
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- VCF, the variant call format
- Danecek P, Auton A, Abecasis G, Albers CA, Banks E, DePristo MA, et al. (2011) The variant call format and VCFtools. Bioinformatics 27(15): 2156–2158. doi:10.1093/bioinformatics/btr330
- GA4GH and the SAMtools team (hts-specs). The Variant Call Format specification, VCFv4.4. samtools.github.io/hts-specs/VCFv4.4.pdf
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
pxil pictures of sequences
A pxil file holds one sequence – a gene, a transcript, a protein, or any other string of letters – as a picture: one pixel a residue, in rows like a page of text. It is an ordinary PNG, so anything that shows pictures shows it, and its pixels are the residues themselves, so the sequence comes back exactly. gpxil is for whole genomes; pxil is for single sequences, to look at, share and compare.
Pixels are residues
A pxil file is an 8-bit indexed-colour PNG. Each pixel’s value is a residue’s place in the file’s alphabet, a string of up to 256 letters stored with it: value i is the alphabet’s letter i. Residue i, counting from 0, is in row i ÷ width and column i mod width, and the pixels after the last residue are 0, which is kept for padding. The PNG’s palette gives each value a colour, so that the picture can be seen, but the colours mean nothing to a reader: change them, and the sequence is the same.
Two alphabets are standard:
| Alphabet | Letters | Colours, as gpxil2pxil and pxil make them |
| DNA, 32 letters | -ACMGRSVTWYHKDBN-acmgrsvtwyhkdbn | A red, C blue, G green, T yellow, N near-black, ambiguity codes mixed, and soft-masked (lower-case) bases paler |
| protein, 28 letters | -ACDEFGHIKLMNPQRSTVWYUOBZJX* | Taylor’s colours, one for each amino acid, and the stop codon * near black |
The DNA alphabet is gpxil’s ref code (The codes), so a base’s pixel value is the same in both formats. Any other alphabet will do too, such as -ACGUN for RNA.
What it says about itself
The PNG has a text chunk (iTXt) named pxil, which holds JSON:
| Keys |
| required | pxil (the version, "0.1"), kind (gene, transcript, protein or sequence), alphabet, length, width |
| recommended | id, name, source, created, generator, and sha256, the sequence’s checksum |
| for a place in a genome | assembly, contig, start and end (counting from 0, the end not included), strand, orientation, gene, transcript, protein, exons, cds, genetic_code, palette, description |
| added by gpxil2pxil | sample and haplotype, for a sequence made from a person; notes, such as differs from the reference: P72R; and a transcript’s biotype and tags (Ensembl canonical, MANE Select, MANE Plus Clinical) |
A gene’s picture is its genomic sequence, introns included, 5′ to 3′. A transcript’s is spliced, 5′ to 3′, and written with T, as in Ensembl’s cDNA files. A protein’s runs from its N terminus to its C terminus, with the stop codon as *.
Names
pxil files are named label.kind.pxil.png, such as TP53-201_ENSP00000269305.protein.pxil.png, with the database’s ID in the label; gpxil2pxil adds the sample and haplotype, as in TP53-201_ENSP00000269305_HG002-hap1.protein.pxil.png. The name ends in .png so that computers treat the file as the picture it is. Readers go by the metadata, though, not the name.
Looking at one
Open a pxil file in the Files tab, and it is shown large, pixel by pixel, with its metadata – its kind, IDs and length, where it comes from, its notes – and its sequence, read from the pixels in the page, without Python. Point at a pixel, and the line under the picture names the residue and its number, such as amino acid 72: R (arginine). Copy the sequence puts the sequence on the clipboard, and As FASTA (pxil) opens pxil pictures → FASTA.
pxil and gpxil
| gpxil | pxil |
| holds | whole genomes, and one person’s variants | one sequence |
| file | TIFF: .gpxil.tif | PNG: .pxil.png |
| residues | DNA, as gpxil codes, in several planes | any alphabet: DNA, RNA, protein … |
| coordinates | every pixel is a position in the genome | optional, in the metadata |
| for | storing, exchanging and analysing genomes | looking at, sharing and analysing single sequences |
pxil is deliberately small and permissive. The Gpxil browser offers its transcripts and proteins as pxil files, and gpxil2pxil makes them from any gpxil file (Genes, transcripts and proteins as pxil pictures).
In Python
The pxil package reads and writes pxil files, but Pillow alone is enough to read one. This makes the reference’s TP53 protein with the example GTF, and reads it back both ways:
import json
from PIL import Image
import pxil
from gpxiltools import GpxilFile
from gpxil2pxil import GTF, make
gene = GTF("/data/examples/TP53.gtf").find("TP53")
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
f = make(g, gene, kinds=["protein"], haplotypes=(None,), out_dir="/home/user/results/pxil/TP53-ref")[0]
print(f.path)
# with Pillow alone: the pixels' values, row by row, are places in the alphabet
im = Image.open(f.path)
meta = json.loads(im.info["pxil"])
indices = im.tobytes()[: meta["length"]]
sequence = "".join(meta["alphabet"][i] for i in indices)
print(meta["kind"], meta["name"], meta["length"], im.size, sequence[:20])
# with the pxil package
seq, meta = pxil.read(f.path)
print(seq == sequence, "amino acid 72:", seq[71])
pxil.write("/home/user/results/my-rna.pxil.png", "AUGGCUUGA", alphabet="-ACGUN", id="my-rna")
print(pxil.read("/home/user/results/my-rna.pxil.png"))
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
- Pillow, which writes the PNG pictures
- Jeffrey A. Clark and contributors. Pillow, the friendly PIL fork. python-pillow.github.io
- Ensembl (GRCh38 sequence, and genes from release 116)
- Yates AD, Austine-Orimoloye O, Azov AG, Barba M, Barnes I, Barrera-Enriquez VP, et al. (2026) Ensembl 2026. Nucleic Acids Research 54(D1): D1053–D1060. doi:10.1093/nar/gkaf1239
Big files and memory
A gpxil file of a chromosome is tens of megabytes, and of a whole genome nearly a gigabyte, while a browser tab has a few gigabytes at most – and Python less. So the studio never copies a big file whole: everything reads just the part it needs. This page explains how, what the limits are, and how to stay within them. A whole genome without running out of memory walks through it with examples.
The format makes it possible
Each plane of a gpxil file is stored in strips of 32 rows – 262,144 bases – each compressed on its own (The gpxil format). A reader opens a file by reading its metadata and the TIFF’s list of pages, a few kilobytes, and then decompresses only the strips a region needs. A gene is a strip or two, whether the file holds one chromosome or the whole genome.
Without Python: the Viewer and the Files tab
The Viewer and the Files tab read gpxil files themselves, in the page: the list of pages, then only the strips they show, decompressed by the browser. A file in your home folder is read where the browser keeps it, and a file on the web, such as GRCh38’s chromosomes, with HTTP range requests, which fetch just those bytes. So any gpxil file opens at once. The Viewer draws at most 4 million bases at a time.
Python: big files are lent, not copied
Python runs in a web worker – Pyodide, CPython compiled to WebAssembly – with a memory of its own. Small files are copied into it before a cell or a command runs, and the changes copied back afterwards. Files of 8 MB or more never are:
- Your big files stay where the browser keeps them, and are lent to Python as files it reads a piece at a time.
- The examples and the GRCh38 reference are read from the web a megabyte at a time, as Python asks for them, with HTTP range requests.
- A big file that Python writes, such as a new gpxil file, is handed to the browser to keep when the command ends, and Python’s copy is freed.
gpxiltools reads only the strips of the region it is asked for, so gpxil info on chromosome 1 reads a few kilobytes, and a gene a few hundred. In the experiment that led to the studio, the whole genome as one file, 809 MB, opened in 72 MB of Python’s memory.
Working copies
To work on part of a big file, copy that part: gpxil subset makes a new gpxil file of the contigs you choose, and of the planes you choose, copying the pages’ strips as they are stored, so it takes about one strip of memory however big the file (Make a working copy). A chromosome of a whole genome is a few per cent of it, and without the reference plane a hundredth of that again. gpxil stats counts what a file holds a piece at a time in the same way (Count what a file holds).
What does take memory
| What | How much | What to do |
Make a gpxil file (gpxil encode) | each contig’s planes, whole, while they’re made: about 6 bytes a base with gt and flags, so 0.5 GB for chromosome 17 and about 1.5 GB for chromosome 1; gq, dp and ps as much again | one chromosome at a time (Only these contigs, --contigs); the biggest chromosomes, or a whole genome, on your own computer |
| a whole plane read at once | a byte a base (4 for ps): c.read("gt") on chromosome 1 is 249 MB | read in pieces, and keep only what you work out from each |
a picture (gpxil render) | held whole: in the browser, at most 25 million bases and 64 million pixels | a smaller region, or fewer pixels a base |
FASTA (gpxil fasta) | text, a byte a base: in the browser, at most 50 million bases in one go | a region at a time |
a book (gpxil book) | a picture for each page: hundreds of pages take a while and a lot of memory | a proof first (At most this many pages), and a chromosome at a time |
On your own computer, the same commands have none of the browser’s limits, only your computer’s.
Keeping your own code small
- Read regions –
c.read(plane, start, end) – not whole planes, and go through a chromosome in pieces: a strip (262,144 bases) or a megabase at a time.
- Keep arrays as they come, 8-bit (
uint8): chromosome 1’s plane as 64-bit numbers is 2 GB instead of 249 MB. Count with np.count_nonzero rather than building big arrays of results.
del big arrays you’ve finished with. Python’s memory doesn’t shrink, but what is freed is used again.
- If Python stops with a MemoryError, or stops answering, restart it: Restart in the Notebook, or the Python pill in the Tools tab. Your files are safe; only Python’s variables are lost.
This counts HG002’s substitutions on chromosome 17, a strip at a time, so that it never holds more than a quarter of a megabyte of the plane:
import numpy as np
from gpxiltools import GpxilFile
from gpxiltools import codes as C
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
c = g["chr17"]
step = 32 * c.width # one strip: 262,144 bases
het = hom = pieces = 0
for a in range(0, c.length, step):
gt = c.read("gt", a, min(a + step, c.length))
h1, h2 = C.hap1(gt), C.hap2(gt)
s1, s2 = np.isin(h1, C.HAP_SUB_CODES), np.isin(h2, C.HAP_SUB_CODES)
hom += np.count_nonzero(s1 & s2 & (h1 == h2))
het += np.count_nonzero((s1 & (h2 == C.HAP_REF)) | (s2 & (h1 == C.HAP_REF)) | (s1 & s2 & (h1 != h2)))
pieces += 1
print(f"{het:,} heterozygous and {hom:,} homozygous substitutions, read in {pieces} pieces")
The browser’s storage
Your files are kept in the browser’s storage for the site (Where your files are kept). A single file bigger than 1 GB is there while the page is open but isn’t kept, so download it before you leave. The studio warns when your files come to more than 2,000 MB, and browsers limit what a site may keep, usually to a share of the free disk space. A whole genome with one person fits, but not many: for many people, keep files without the reference plane (--no-ref), a small fraction of the size, which borrow GRCh38 from the web when they need its bases.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- Pyodide (Python in the browser)
- The Pyodide development team. Pyodide: Python with the scientific stack, compiled to WebAssembly. pyodide.org
- NumPy
- Harris CR, Millman KJ, van der Walt SJ, Gommers R, Virtanen P, Cournapeau D, et al. (2020) Array programming with NumPy. Nature 585(7825): 357–362. doi:10.1038/s41586-020-2649-2
- tifffile, which gpxiltools reads and writes TIFF with
- Gohlke C. tifffile: read and write TIFF files. github.com/cgohlke/tifffile
The GRCh38 reference
/data/grch38 holds the human reference genome, GRCh38, as gpxil files: one for each chromosome, and one for the unplaced scaffolds. They are the Gpxil browser’s files, read from its web site a piece at a time as they are used, so they are there at once without being downloaded. Every tool and the Viewer can use them: as the reference to encode a VCF against, as a genome to look at or draw, or to make a gene’s pictures from.
What it is
- The primary assembly of GRCh38, from Ensembl release 116 (
Homo_sapiens.GRCh38.dna_sm.primary_assembly.fa.gz): chromosomes 1 to 22, X, Y and the mitochondrion, and 169 unplaced and unlocalised scaffolds, such as KI270728.1. There are no alternative haplotypes or patches.
- Soft-masked: bases in repeats are lower case in Ensembl’s FASTA file, and marked as soft-masked in the
ref plane, so the Viewer shows them paler.
- The reference plane only, with no sample, written by gpxiltools 0.2.0 at 8,192 bases a row.
- Named
chr1 … chr22, chrX, chrY and chrM, with Ensembl’s names (1 … MT) as aliases, so a VCF that says 17 finds chr17. Each has its MD5 and refget checksums, which a file written without its reference plane is checked against.
| Files | Size |
chr1.gpxil.tif … chr22.gpxil.tif, chrX.gpxil.tif, chrY.gpxil.tif | from 70.5 MB (chromosome 2) and 66.7 MB (chromosome 1, more of which is unknown, N) down to 7.3 MB (Y) |
chrM.gpxil.tif | 7 KB, for 16,569 bases |
unplaced.gpxil.tif | 3.1 MB, for the 169 scaffolds |
| all 26 | 848.5 MB |
Read from the web, a piece at a time
The files live on the Gpxil browser’s web site, pixelverse.cam/data/grch38/. Nothing is downloaded whole: the Viewer and the Files tab fetch the strips they show, and Python reads a megabyte at a time, with HTTP range requests, as it needs to (Big files and memory). The first look at a chromosome takes a moment, and reading the reference needs a connection to the web.
A file of your own on GRCh38 written without its reference plane (--no-ref) borrows its bases from here: the Viewer does so by itself, the tools with Its reference, which they fill in for you, and gpxiltools with reference=. The checksums make sure it is the same reference.
Using it
- To encode a VCF called on GRCh38: give the chromosome’s file to Make a gpxil file as the reference, a chromosome at a time (the walkthrough). Only the contigs encoded are read from it.
- To look at: choose a chromosome in the Viewer, under GRCh38 reference.
- To draw or print:
gpxil render and gpxil book take any region of it.
- For genes: gpxil2pxil makes any gene’s, transcript’s or protein’s picture from it, such as CFTR’s from
chr7.gpxil.tif.
gpxil info /data/grch38/chr21.gpxil.tif
gpxil fasta /data/grch38/chr17.gpxil.tif --region chr17:7676101-7676200
gpxil render /data/grch38/chrM.gpxil.tif chrM -o ~/results/chrM.png --width 128 --scale 2
Its genes
The Gpxil browser also has the genes of Ensembl release 116 on GRCh38 – all 78,941 of them, and their 646,577 transcripts – with the canonical and MANE transcripts marked, and HGNC’s symbols, names and aliases. gpxil2pxil reads them a gene at a time when you name a gene (Genes, transcripts and proteins as pxil pictures). To work without the web, or with another annotation, give it a GTF file instead.
The whole genome in one file
For your own computer, the Gpxil browser offers the whole of GRCh38 as one gpxil file, GRCh38.gpxil.tif (848.5 MB), with its SHA-256 checksum beside it. Download it from the command line, check it, and use it like any other gpxil file:
curl -LO https://github.com/comparativechrono/Gpxil-browser/releases/download/grch38-ensembl116/GRCh38.gpxil.tif
curl -LO https://github.com/comparativechrono/Gpxil-browser/releases/download/grch38-ensembl116/GRCh38.gpxil.tif.sha256
sha256sum -c GRCh38.gpxil.tif.sha256 # on a Mac: shasum -a 256 -c GRCh38.gpxil.tif.sha256
gpxil info GRCh38.gpxil.tif
If the download stops, the same command with -C - after curl carries on from where it stopped. A chromosome at a time comes from the files the studio reads, such as curl -O https://pixelverse.cam/data/grch38/gpxil/chr21.gpxil.tif. With the whole genome on your own computer, gpxil encode GRCh38.gpxil.tif -v my.vcf.gz --callable my.bed --no-ref -o my.gpxil.tif encodes a person’s whole genome in one go (On your own computer). In the studio there is no need: it reads the reference from the web as it is.
Where it comes from
GRCh38 is the Genome Reference Consortium’s assembly of the human genome. The sequence, its soft-masking and the genes are Ensembl’s, release 116, and the gene names HGNC’s. The Gpxil browser made the gpxil files with gpxiltools 0.2.0. Cite the assembly and Ensembl when you use them:
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- The GRCh38 reference genome
- Schneider VA, Graves-Lindsay T, Howe K, Bouk N, Chen HC, Kitts PA, et al. (2017) Evaluation of GRCh38 and de novo haploid genome assemblies demonstrates the enduring quality of the reference assembly. Genome Research 27(5): 849–864. doi:10.1101/gr.213611.116
- Ensembl (GRCh38 sequence, and genes from release 116)
- Yates AD, Austine-Orimoloye O, Azov AG, Barba M, Barnes I, Barrera-Enriquez VP, et al. (2026) Ensembl 2026. Nucleic Acids Research 54(D1): D1053–D1060. doi:10.1093/nar/gkaf1239
- MANE transcripts
- Morales J, Pujar S, Loveland JE, Astashyn A, Bennett R, Berry A, et al. (2022) A joint NCBI and EMBL-EBI transcript set for clinical genomics and research. Nature 604(7905): 310–315. doi:10.1038/s41586-022-04558-8
- The Gpxil browser, and its GRCh38 files and gene files
- Chronomic Medicine Group, University of Cambridge (2026) The Gpxil browser: the GRCh38 genome as gpxil pictures, in a web browser. pixelverse.cam
The Notebook
The Notebook tab runs Python notebooks in your browser, with gpxiltools, gpxil2pxil and pxil installed, and NumPy and Pillow beside them. They’re the same .ipynb files that Jupyter uses. Use a notebook for what the forms don’t do: your own questions of a genome, the same steps over many files, or a record of an analysis that anyone can run again.
The notebook to start from
~/notebooks/gpxil-in-python.ipynb goes through gpxil files in Python, on HG002’s chromosome 17: opening a file, a region of each plane as a NumPy array, the codes, each variant base as the file holds it, HG002’s P72R in TP53, his two sequences, a picture, the whole chromosome a piece at a time, the GRCh38 reference from the web, pictures of genes with gpxil2pxil and pxil, and the programs run from a cell. Run its cells from the top. If you’ve changed or deleted it, delete it (or rename it) and press Open the starting notebook for a fresh copy.
Work with a gpxil file pixel by pixel, in Python covers the same ground step by step, and The packages in Python lists the main functions of each package.
Working in a notebook
- Run a cell with Shift+Enter, or press Run all to run every cell from the top. The first cell you run starts Python, which takes a few seconds; the very first time, it also downloads about 15 MB, which your browser then keeps.
- Code and Text add cells; text cells are written in Markdown. A cell’s ⋯ menu moves, duplicates, clears or deletes it, or turns it from code into text and back.
- Stop stops a cell that is taking too long. It restarts Python to do so, so the notebook’s variables are forgotten. Restart does the same at any time, and the list button shows the variables the notebook has now.
- The notebook is saved among your files as you work, outputs and all. Download gives you a copy as a Jupyter notebook (
.ipynb), a Python script, or a web page with its results. The notebook opens in Jupyter, JupyterLab, VS Code or Google Colab on a computer where the packages are installed (On your own computer).
- As in Jupyter, a notebook starts in its own folder. It sees the same files as the other tabs: your home folder,
/home/user (~), the examples in /data/examples, and the GRCh38 reference in /data/grch38.
- To open a notebook you’ve made elsewhere, drag it onto the Notebook tab, or add it in the Files tab and click it.
What a cell shows
As in Jupyter, a cell shows what it prints, and the value of its last line: a number, a NumPy array, a dictionary. A Pillow picture as the last line is shown as a picture, with a button to save it as a PNG file, so render(…).to_rgb() made into an Image shows a region at once. matplotlib is loaded in the background once Python has started, and its plots are shown under the cell too. display(…) shows several things from one cell.
from PIL import Image
from gpxiltools import GpxilFile
from gpxiltools.render import render
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
picture = render(g["chr17"], mode="variants", start=7_661_778, end=7_687_546, bases_per_row=256)
Image.fromarray(picture.scaled(2).to_rgb())
Commands in a cell
As in Jupyter, a line that starts with ! runs a command, and a line that starts with % is one of the notebook’s own commands. gpxil, gpxil2pxil, pxil and python run in the notebook’s own Python, at once; other commands run in the studio’s shell, as they would in the Terminal tab.
!gpxil info /data/examples/worked-example.gpxil.tif
!gpxil fasta /data/examples/worked-example.gpxil.tif --which 2 -o /home/user/results/worked-example.hap2.fa
!cat /home/user/results/worked-example.hap2.fa
| Line | What it does |
!command | Runs a command. files = !ls ~/genomes keeps what it prints, as a list of lines. |
%cd, %pwd, %ls | Change or show the folder, or list the files in it |
%pip install NAME, %pip list | Install a package for this visit: a pure-Python one from PyPI, or one that Pyodide has built for the browser, such as pandas or SciPy. A reload forgets it. |
%time, %%time | How long a line, or the whole cell, takes |
%%writefile FILE | Writes the rest of the cell to a file, such as a script or a BED file |
%%bash | Runs the rest of the cell in the studio’s shell, as a script |
%%capture out | Keeps what the cell prints in out, instead of showing it |
%run script.py, %who, %env, %%html | Work as they do in Jupyter |
Big files in a notebook
A notebook opens a gpxil file without reading it: GpxilFile reads the metadata, and read only the strips of a plane that hold the region you ask for. The studio never copies a file of 8 MB or more into Python’s memory, so a cell can open a whole genome, or a chromosome of the GRCh38 reference on the web, at once. What uses memory is what you read: one byte a base for each plane, so a whole chromosome 1 is 250 MB a plane. Read big regions in pieces, as the starting notebook does for chromosome 17, and let go of arrays you’ve finished with (A whole genome without running out of memory, Big files and memory).
The Tools tab, the Notebook tab and the Terminal tab share one Python. A tool you run waits until a notebook cell has finished, and the other way round. Files that one of them writes are there for the others at once.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- Pyodide (Python in the browser)
- The Pyodide development team. Pyodide: Python with the scientific stack, compiled to WebAssembly. pyodide.org
- NumPy
- Harris CR, Millman KJ, van der Walt SJ, Gommers R, Virtanen P, Cournapeau D, et al. (2020) Array programming with NumPy. Nature 585(7825): 357–362. doi:10.1038/s41586-020-2649-2
- Pillow, which writes the PNG pictures
- Jeffrey A. Clark and contributors. Pillow, the friendly PIL fork. python-pillow.github.io
The packages in Python
These are the main functions of each package, for notebooks and scripts. Every command of the terminal is a function you can call, and every tool in the Tools tab shows its Python under The same in the terminal, and in Python. Add any box below to the open notebook with its button. A package’s own help, such as help(gpxiltools.render.render), tells you more.
Positions in Python count from 0, and the end of a region isn’t included, as in Python’s own slices. So TP53, chr17:7,661,779-7,687,546 as a genome browser writes it, is start=7_661_778, end=7_687_546.
Where things are
| Module | The main functions |
gpxiltools | GpxilFile(path or URL, reference=None) opens a file; g["chr17"] is a contig, with .length, .width, .planes, .read(plane, start, end) (a NumPy array) and .sequence(start, end) (the reference’s letters). g.locate("chr17", start, end) finds a region of a chromosome – in a file of windows, in the window that holds it – and gives the contig and the offsets in it; a window’s .parent and .start say where it lies. GpxilWriter writes files. |
gpxiltools.codes | The codes (The codes): hap1(gt) and hap2(gt) split a genotype into its haplotypes; haplotype_bases, genotype_iupac, dosage (copies of A, C, G and T at each base, for sequence models), ref_to_ascii, describe_gt_byte; the constants HAP_REF, HAP_DEL, HAP_UNKNOWN, FLAG_PHASED and the rest. |
gpxiltools.encode | encode(reference, out, vcf=…, callable_bed=…, contigs=…, regions=…, include_ref=…, planes=…, sex=…): what gpxil encode does; regions=[("chr17", 7_600_000, 7_750_000)] makes windows |
gpxiltools.validate | validate(path): a report with .ok, .errors and .warnings |
gpxiltools.stats | stats(g, regions=None): what gpxil stats counts, per region and in all, as plain data; format_stats makes the table of it; region_tally(contig, start, end) counts one region (Count what a file holds) |
gpxiltools.subset | subset(path, out, contigs, planes=…, include_ref=…, reference=…): what gpxil subset does, with contigs, or regions such as "chr17:7,661,779-7,687,546" to cut out as windows; GpxilWriter.copy_contig copies one contig, and GpxilWriter.cut_contig cuts a window (Make a working copy) |
gpxiltools.compare | compare_pair(a, b, regions=None) and compare_trio(child, father, mother, regions=None): what gpxil compare finds, per region and in all, as plain data; format_compare makes the table of it; picture(files, contig, start, end) draws a region (Compare two genomes, A child and both parents) |
gpxiltools.render | render(contig, mode=…, palette=…, start=…, end=…, bases_per_row=…): a picture, with .scaled(n), .to_rgb(), .to_cmyk(), .save_png(…) and .save_tiff(…). render_arrays draws arrays you’ve made yourself. |
gpxiltools.layout | hilbert_image, and position_to_pixel and pixel_to_position between a base and its pixel |
gpxiltools.book | make_book(g, out, contigs=…, mode=…, colorspace=…, layout=BookLayout(…), max_pages=…): what gpxil book does |
gpxiltools.export | haplotype_sequence, genotype_iupac_sequence and their iter_… forms for long regions, write_fasta_stream, write_vcf, iter_vcf_records |
gpxiltools.palettes | describe(), get_palette(name), ref_colours(palette) |
gpxil2pxil | GTF(path) or BrowserData() (the Gpxil browser’s genes); .find("TP53") gives a gene, with .transcripts, .canonical() and .select(…). Source(g, haplotype) gives sequences: .gene(gene), .spliced(t), .protein(t). make(g, gene, …) writes the pxil files. |
pxil | read(path) gives the sequence and the metadata; write(path, sequence, kind=…, …) makes a pxil file |
Opening a file, with or without its reference
GpxilFile reads only the metadata when it opens a file, and a plane only where you ask for it. A file written without its reference plane (--no-ref) needs the reference to give its bases: a FASTA file, or a gpxil file of the reference such as those in /data/grch38. gpxiltools checks it against the checksums in the file, so the wrong reference can’t be used by mistake.
from gpxiltools import GpxilFile
from gpxiltools.encode import encode
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
print(g.sample_id, g.metadata["reference"]["name"], g.contigs)
c = g["chr17"]
print(f"{c.length:,} bases, {c.width:,} a row; planes {c.planes}")
print(c.sequence(7_676_148, 7_676_160), c.entry["md5"])
# the specification's worked example, written without its reference plane, then read with it
encode("/data/examples/worked-example.fa", "/home/user/genomes/worked-example.noref.gpxil.tif",
vcf="/data/examples/worked-example.vcf", include_ref=False, width=4)
with GpxilFile("/home/user/genomes/worked-example.noref.gpxil.tif", reference="/data/examples/worked-example.fa") as g:
c = g["chrT"]
print(c.planes, c.has("ref"), c.sequence())
A gpxil file on a web server opens just as a path does, and is read a piece at a time with range requests: on your own computer, GpxilFile("https://pixelverse.cam/data/grch38/gpxil/chr17.gpxil.tif") reads chromosome 17 of GRCh38 from the Gpxil browser’s site. In the studio, use /data/grch38/chr17.gpxil.tif, which is the same file.
Arrays, and the codes
import numpy as np
from gpxiltools import GpxilFile
from gpxiltools import codes as C
c = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")["chr17"]
start, end = 7_661_778, 7_687_546 # TP53
ref, gt = c.read("ref", start, end), c.read("gt", start, end)
h1, h2 = C.hap1(gt), C.hap2(gt)
print("bases where haplotype 1 differs from the reference:", int((h1 != C.HAP_REF).sum()))
iupac = C.genotype_iupac(ref, gt) # both haplotypes as one IUPAC code a base
print(C.IUPAC[iupac[7_676_154 - 1 - start]], C.describe_gt_byte(int(gt[7_676_154 - 1 - start])))
d = C.dosage(ref, gt) # copies of A, C, G and T: an (n, 4) array
print(d.shape, d[7_676_154 - 1 - start])
Work with a gpxil file pixel by pixel, in Python goes further: finding variants, counting a whole chromosome, comparing the haplotypes, and writing a file with a plane of your own.
Making, checking and drawing
from gpxiltools import GpxilFile
from gpxiltools.encode import encode
from gpxiltools.validate import validate
from gpxiltools.render import render
summary = encode("/data/examples/worked-example.fa", "/home/user/genomes/worked-example.gpxil.tif",
vcf="/data/examples/worked-example.vcf", width=4)
print(summary)
report = validate("/home/user/genomes/worked-example.gpxil.tif")
print("valid" if report.ok else report.errors)
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
picture = render(g["chr17"], mode="haplotypes", palette="okabe-ito", start=7_676_000, end=7_676_400, bases_per_row=40)
picture.scaled(6).save_png("/home/user/results/TP53_exon4_haplotypes.png")
print(picture.title, picture.shape, [label for label, _ in picture.legend])
For a whole genome, give encode a gpxil reference, such as /data/grch38/chr17.gpxil.tif, and a VCF and BED file on the same reference (Turn someone’s VCF into a gpxil file). make_book, in gpxiltools.book, prints a file as a PDF (Print a chromosome as a book).
Sequences and VCF
from gpxiltools import GpxilFile
from gpxiltools.export import haplotype_sequence, genotype_iupac_sequence, write_vcf
with GpxilFile("/data/examples/HG002_chr17.gpxil.tif") as g:
c = g["chr17"]
print(genotype_iupac_sequence(c, 7_676_140, 7_676_170).decode()) # both haplotypes, in IUPAC letters
print(haplotype_sequence(c, 1, 7_676_140, 7_676_170).decode()) # haplotype 1 alone
with open("/home/user/results/TP53_python.vcf", "w") as out:
n = write_vcf(g, out, regions=[("chr17", 7_661_778, 7_687_546)], nocall_blocks=False)
print(n, "VCF records")
gpxil2pxil: genes, transcripts and proteins
Genes come from a GTF file, or from the Gpxil browser’s gene files (BrowserData(), Ensembl’s genes on GRCh38), which the studio reads from the web. Source gives a sequence from the reference (Source(g)) or from one of the sample’s haplotypes (Source(g, 1)), with the person’s variants in it.
from gpxiltools import GpxilFile
from gpxil2pxil import GTF, Source, make
from gpxil2pxil.sequence import protein_differences
g = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")
gene = GTF("/data/examples/TP53.gtf").find("TP53")
t = gene.canonical() # the Ensembl canonical transcript, TP53-201
print(gene.id, len(gene.transcripts), "transcripts;", t.name, t.id, t.protein)
reference, hap1 = Source(g).protein(t), Source(g, 1).protein(t)
print(len(reference), "amino acids; haplotype 1:", protein_differences(reference, hap1))
for f in make(g, gene, kinds=["protein"], haplotypes=(None, 1, 2), out_dir="/home/user/results/pxil-python"):
print(f.path, f.length, f.notes)
pxil files
A pxil file is a PNG picture of one sequence, so pxil.read needs nothing but Pillow. pxil.write makes one from any sequence: kind is gene, transcript, protein or sequence, and other keywords go into its metadata, such as sample and haplotype for a person’s own version of a sequence. pxil.fasta_name gives the name pxil gives it in a FASTA file.
import pxil
from gpxiltools import GpxilFile
from gpxil2pxil import GTF, Source
g = GpxilFile("/data/examples/HG002_chr17.gpxil.tif")
t = GTF("/data/examples/TP53.gtf").find("TP53").canonical()
protein = Source(g, 1).protein(t) # TP53's protein from HG002's first haplotype
pxil.write("/home/user/results/TP53_HG002-hap1.pxil.png", protein, kind="protein", id=t.protein, name=t.name,
sample=g.sample_id, haplotype=1, source="HG002_chr17.gpxil.tif")
sequence, meta = pxil.read("/home/user/results/TP53_HG002-hap1.pxil.png")
print(sequence == protein, meta["length"], meta["width"], sequence[60:80])
print(pxil.fasta_name(meta))
Many files at once
import glob, os
from gpxiltools import GpxilFile
for path in sorted(glob.glob("/data/examples/*.gpxil.tif") + glob.glob("/home/user/genomes/*.gpxil.tif")):
with GpxilFile(path) as g:
bases = sum(c.length for c in g)
print(f"{os.path.basename(path):36} {g.sample_id or '(the reference)':16} {len(g.contigs)} contig(s), {bases:>12,} bases")
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- NumPy
- Harris CR, Millman KJ, van der Walt SJ, Gommers R, Virtanen P, Cournapeau D, et al. (2020) Array programming with NumPy. Nature 585(7825): 357–362. doi:10.1038/s41586-020-2649-2
The Terminal
The Terminal tab is a Linux-style shell in your browser. gpxil, gpxil2pxil and pxil are commands in it, and they run exactly as they do on a computer where the packages have been installed with pip, with the same options and the same output. Every tool in the Tools tab shows its command, with a button to run it here.
The gpxil programs
| Command | What it does |
gpxil encode | a reference (FASTA, or a gpxil file such as those in /data/grch38) and a person’s VCF → a gpxil file (Make a gpxil file) |
gpxil info | what a gpxil file holds; --json prints all its metadata |
gpxil validate | checks a file against the specification; it ends with status 1 if the file isn’t valid |
gpxil stats | counts what a file holds – bases called, substitutions and Ti/Tv, insertions, deletions, phasing, quality – for every contig or for regions; --json for programs |
gpxil subset | copies some contigs, and planes, into a new gpxil file: a working copy, made without decoding (--no-ref leaves out the reference plane); a region, such as chr17:7,661,779-7,687,546, is cut out as a window |
gpxil compare | two genomes base by base, the second as the truth: what is the same, written differently or different, with precision and recall; or a child with --father and --mother: Mendelian errors, new mutations, where each variant came from, crossovers. -o draws a region of it |
gpxil render | a contig or a region as a picture: PNG for the screen, TIFF in RGB or CMYK for print |
gpxil book | a PDF to print, one dot per base |
gpxil fasta | the reference’s sequence, one of the person’s haplotypes (--which 1 or 2), or their genotype in IUPAC letters (--which iupac), as FASTA |
gpxil vcf | the person’s genotypes as VCF, for the whole file or --regions of it |
gpxil palettes | the colours of each palette, on screen and in print |
gpxil2pxil | pictures (pxil files) of genes, transcripts and proteins, from the reference or from each of the person’s haplotypes; --list lists a gene’s transcripts |
pxil | pxil files as FASTA; --info prints their metadata |
viewer FILE [REGION] | opens a gpxil file in the Viewer tab, at a region if you give one; --mode=haplotypes (or reference, genotype, variants) chooses the view, as for gpxil render, and --plane=gq shows a plane |
man NAME shows examples, and NAME --help the options, from the program itself: gpxil render --help, for instance. Regions are written as genome browsers write them, chr17:7,661,779-7,687,546, counting from 1, with both ends included; commas are optional. Each program also runs as python -m gpxilwb NAME …, and gpxil as python -m gpxiltools.cli ….
cd ~/results
gpxil info /data/examples/HG002_chr17.gpxil.tif
gpxil fasta /data/examples/HG002_chr17.gpxil.tif --which iupac --region chr17:7,676,101-7,676,200 -o TP53_exon4.fa
cat TP53_exon4.fa
gpxil vcf /data/examples/HG002_chr17.gpxil.tif --region chr17:7,661,779-7,687,546 --no-nocall-blocks -o TP53.vcf
grep -v '^#' TP53.vcf | cut -f 1,2,4,5,10 | head
gpxil render /data/examples/HG002_chr17.gpxil.tif chr17:7,661,779-7,687,546 --mode variants --width 256 --scale 2 -o TP53.png
ls -l
The shell
- It’s bash: pipes, redirection, variables, loops, functions,
$( ), here-documents and scripts (bash script.sh). ↑ and ↓ bring back earlier commands, Tab completes names, Ctrl+L clears the screen.
- Ctrl+C stops a command: the program that’s running finishes, and the rest of the line or script is skipped. Press it again to stop the program at once. For gpxil, gpxil2pxil, pxil and
python, that restarts Python, so a notebook’s variables are forgotten too.
- The text programs –
cat, head, tail, grep, sed, cut, sort, uniq, wc, tr, paste, diff, column, less and others – were written for this terminal after the GNU ones. NAME --help says which options each has. So were the file programs: ls, cp, mv, rm, mkdir, find, du, tree and the rest, and zip, unzip and tar.
python runs scripts, python -c "…" and python -m json.tool FILE, in the same Python as the notebooks. pip install NAME adds a package for this visit.
curl and wget download through the browser, from servers that let web pages read them: the Gpxil browser, Ensembl’s REST API, raw.githubusercontent.com, EBI and Zenodo, among others. You don’t need them for GRCh38, which is in /data/grch38 already.
open FILE shows a file in its tab, nano FILE edits it in the Files tab, and download FILE (or a folder, as a .zip) saves it to your computer.
- Not here: background jobs (
&), sudo, apt and ssh, and programs that aren’t in help’s list, such as bcftools and samtools. gpxil does much of their work on gpxil files: gpxil vcf --region cuts out a region as VCF, and gpxil fasta --which 1 spells out a haplotype.
Save pictures with
-o, not
>The shell’s redirection (> and |) is for text. Pictures, PDFs and gpxil files are written properly only by the program itself, so give their names with -o, as every example here does.
Several at once
A loop does the same to many regions or files. These commands spell out the bases around TP53’s codon 72 from the reference and from each of HG002’s haplotypes, then make pictures of TP53’s protein from the reference and from both haplotypes, with the transcripts from a GTF file:
mkdir -p ~/results/P72
for which in ref 1 2; do
gpxil fasta /data/examples/HG002_chr17.gpxil.tif --which $which --region chr17:7,676,140-7,676,170 -o ~/results/P72/P72.$which.fa
done
cat ~/results/P72/*.fa
gpxil2pxil /data/examples/HG002_chr17.gpxil.tif TP53 -a /data/examples/TP53.gtf --kinds protein --haplotypes all -o ~/results/P72
pxil ~/results/P72/*.pxil.png -o ~/results/P72/TP53-proteins.fa
grep '>' ~/results/P72/TP53-proteins.fa
gpxil2pxil says how each haplotype’s protein differs from the reference’s (P72R), and pxil puts the three proteins into one FASTA file, each named by its Ensembl ID and, for a haplotype, the person and the haplotype, ready for an alignment program. Without -a, gpxil2pxil takes Ensembl’s genes and transcripts from the Gpxil browser’s gene files, so any gene of GRCh38 can be named: gpxil2pxil /data/grch38/chr17.gpxil.tif BRCA1 --kinds protein -o ~/results.
Keeping a session
export-session session.ipynb saves the commands of this session, with what they printed, as a notebook, one %%bash cell a command; so does the Save session button above the terminal. export-session session.sh saves them as a script, which runs in Jupyter or a terminal on any computer where the packages are installed (On your own computer).
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
On your own computer
The packages that run here are ordinary Python packages, so every command and every piece of Python that the studio shows you works on your own computer too: in a pipeline, on a server, or on a whole genome at once, with as much memory as the computer has rather than what a browser tab allows.
Installing them
gpxiltools, gpxil2pxil and pxil are on PyPI, at the versions the studio runs (Versions, and how to cite), so pip installs them. gpxiltools and gpxil2pxil need Python 3.10 or later, pxil 3.9 or later; pip installs what they need with them (NumPy, tifffile, imagecodecs, Pillow and jsonschema).
pip install gpxiltools gpxil2pxil pxil
To try changes that aren’t released yet, install a package from its repository on GitHub instead, such as pip install "git+https://github.com/comparativechrono/Gpxil-tools".
Each package installs its command: gpxil, gpxil2pxil and pxil. They take the same options as in the Terminal tab, so the commands the Tools tab shows work on your computer once you change the paths to those of your own files. In Python, the imports are the ones in The packages in Python. The notebooks you download from the Notebook tab run in Jupyter as they are, apart from paths such as /data/examples: download the files they read too, from the Files tab.
The whole genome
The GRCh38 reference that the studio reads a chromosome at a time from the Gpxil browser is also one file of the whole genome, about 850 MB. Download it once, and give it to gpxil encode as the reference for a whole genome’s VCF:
curl -LO https://github.com/comparativechrono/Gpxil-browser/releases/download/grch38-ensembl116/GRCh38.gpxil.tif
gpxil encode GRCh38.gpxil.tif -v HG002.vcf.gz --callable HG002.callable.bed --sex XY --no-ref -o HG002.gpxil.tif
gpxil render HG002.gpxil.tif chr21 --mode variants -o chr21.png --reference GRCh38.gpxil.tif
gpxil stats HG002.gpxil.tif --reference GRCh38.gpxil.tif # what it holds, chromosome by chromosome
gpxil subset HG002.gpxil.tif chr17 chr21 -o HG002_17_21.gpxil.tif # a working copy to add to the studio
gpxil compare HG002.gpxil.tif --father HG003.gpxil.tif --mother HG004.gpxil.tif --json > trio.json
Encoding holds a chromosome’s planes in memory while it works, about 6 bytes a base, so the largest, chromosome 1, needs about 1.5 GB. HG002’s whole genome took 12 minutes and at most 2 GB of memory on an ordinary computer, and made a file of 888 MB with the reference plane. The Gpxil browser’s downloads also have each chromosome on its own, and a checksum to check the download with. Files without their reference plane (--no-ref) are a hundred times smaller; give the reference with --reference to the commands that need its bases (The GRCh38 reference).
To work on part of a whole genome here, make a working copy of the chromosomes you need with gpxil subset, which copies them without decoding, and add the copy with Add a file… in the Viewer or the Files tab (Make a working copy). The studio can also take the whole file and copy from it itself: it reads only the pages it copies.
The studio’s own copies
You can also download the exact copies of the packages that the studio runs, in one archive with tifffile and the studio’s own runner, gpxilwb. This is handy on a computer that can’t reach PyPI or GitHub. It needs NumPy, Pillow and jsonschema (pip install numpy pillow jsonschema), and imagecodecs only for files compressed with Zstandard or LZW.
unzip gpxil-tools.zip -d gpxil-tools
export PYTHONPATH="$PWD/gpxil-tools"
python -m gpxilwb gpxil info HG002_chr17.gpxil.tif
python -m gpxilwb gpxil2pxil HG002_chr17.gpxil.tif TP53 --kinds protein -o pictures
python -m gpxilwb PROGRAM … runs any of the three programs, as the terminal here does; python -m gpxiltools.cli, python -m gpxil2pxil and python -m pxil run them too.
Running the studio itself
GpxilStudio is a web page with no server behind it, so you can run your own copy: download its repository from GitHub and start the small server that comes with it, which answers range requests as GitHub Pages does.
python3 serve.py 8000 # then open http://localhost:8000/
python3 serve.py 8000 --reference DIR # the GRCh38 reference from a copy on this computer
With --reference, the studio reads /data/grch38 from a copy of the Gpxil browser’s data/grch38 folder, so it needs the web only for Python itself, the first time.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- The GRCh38 reference genome
- Schneider VA, Graves-Lindsay T, Howe K, Bouk N, Chen HC, Kitts PA, et al. (2017) Evaluation of GRCh38 and de novo haploid genome assemblies demonstrates the enduring quality of the reference assembly. Genome Research 27(5): 849–864. doi:10.1101/gr.213611.116
Versions, and how to cite
These are the versions that this copy of the studio runs. When you report work done with it, say which versions you used. The files record them too: a gpxil file names the program that wrote it in its metadata ("generator": "gpxiltools 0.7.0"), with the version of the specification it follows, and a pxil file does the same.
| Package | Version |
| gpxiltools | 0.8.2 |
| gpxil2pxil | 0.2.0 |
| pxil | 0.1.1 |
| tifffile (which gpxiltools reads and writes TIFF with) | 2026.10.15 |
| gpxilwb (the studio's own runner, not on PyPI) | 1.1.0 |
Python itself is Pyodide 0.29.5 (Python 3.13), with NumPy 2.2.5, Pillow 11.3.0 and jsonschema 4.23.0 as Pyodide builds them, downloaded from jsDelivr the first time Python starts; matplotlib 3.8.4 follows in the background, for plots. To see what you have, type python --version or pip list in the terminal, or help, which gives the programs’ versions once Python has started.
The packages
- gpxiltools 0.8.2 reads gpxil files on web servers a piece at a time (with HTTP range requests), takes a gpxil file as the reference, such as a chromosome of GRCh38 from the Gpxil browser, writes VCF of chosen regions (
gpxil vcf --region), and runs without imagecodecs, which Pyodide doesn’t have: all new in 0.3.0. 0.3.1 gives N, a base that isn’t known, a colour that no base or mix of bases has: near-black in the classic and Okabe–Ito palettes, where it was a grey that a heterozygous C/T base shared. 0.4.0 adds gpxil subset, working copies of some contigs and planes made without decoding, and gpxil stats, the counts of what a file holds. 0.5.0 adds gpxil compare: two genomes base by base, and a child with both parents. 0.6.0 writes version 0.2 of the gpxil specification, in which unphased calls that overlap go one to each haplotype rather than running together on one, and has the reader of gpxil files in JavaScript that the Viewer and the Files tab use, the same as the Gpxil browser’s. 0.7.0 writes version 0.3, which adds windows: contigs that hold a stretch of a chromosome and keep its positions. gpxil subset cuts a region out as one, gpxil encode --region makes one straight from a VCF, and every command, the JavaScript reader and the Viewer read them at the chromosome’s positions. 0.8.0 adds six tools that look for patterns: runs of a composition (gpxil runs), repeats wrapped at their period (gpxil period), features stacked and lined up (gpxil stack), substitution spectra matched by context (gpxil spectrum), annotation planes from BED or RepeatMasker files (gpxil annotate) and strand-symmetry maps (gpxil skew). 0.8.1 takes a repeat’s class and family from its table, so repeats whose own names hold a slash are labelled rightly. 0.8.2 reads a person’s haplotypes faster: a contig’s inserted sequences are decoded once, not for every region read.
- gpxil2pxil 0.2.0 makes pxil pictures of genes, transcripts and proteins from a gpxil genome, from the reference or from either of a person’s haplotypes, with the genes of Ensembl from the Gpxil browser’s files or from a GTF file, and says how a person’s protein differs from the reference’s. 0.2.0 finds a gene in a window that holds it.
- pxil 0.1.1 reads and writes pxil files, and has a command,
pxil, that prints them as FASTA. 0.1.1 colours N near-black, as gpxiltools does.
- gpxilwb 1.1.0 is the studio’s own runner, which joins the page to the packages. You don’t need it anywhere else.
The studio’s copies of gpxiltools, gpxil2pxil and pxil are their releases on PyPI, unchanged, so pip install gpxiltools gpxil2pxil pxil on your own computer gives exactly the code that runs here (On your own computer). The formats have versions too: the examples here follow version 0.2 of the gpxil specification, except the TP53 window, which follows 0.3 (the first with windows), and the GRCh38 reference’s files, which hold no person, version 0.1 (the three differ only in how a person’s calls are written, and in windows); pxil files follow version 0.1 of the pxil specification.
The data
- The GRCh38 reference in
/data/grch38 is the Gpxil browser’s: the primary assembly of GRCh38 from Ensembl release 116, soft-masked, with Ensembl 116’s genes and transcripts.
- HG002 in the examples is the Genome in a Bottle benchmark for HG002, version 4.2.1 on GRCh38, with its regions called with confidence; HG003 and HG004, his father and mother, are their version 4.2.1 benchmarks, and
HG002_chr17_v5.0q is HG002’s newer benchmark, version 5.0q. Cite Zook et al. 2016 and Wagner et al. 2022 if you use them, and Hansen et al. 2026 for version 5.0q.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- The Gpxil browser, and its GRCh38 files and gene files
- Chronomic Medicine Group, University of Cambridge (2026) The Gpxil browser: the GRCh38 genome as gpxil pictures, in a web browser. pixelverse.cam
- Genome pixel images (GPxI)
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
- The GRCh38 reference genome
- Schneider VA, Graves-Lindsay T, Howe K, Bouk N, Chen HC, Kitts PA, et al. (2017) Evaluation of GRCh38 and de novo haploid genome assemblies demonstrates the enduring quality of the reference assembly. Genome Research 27(5): 849–864. doi:10.1101/gr.213611.116
- Ensembl (GRCh38 sequence, and genes from release 116)
- Yates AD, Austine-Orimoloye O, Azov AG, Barba M, Barnes I, Barrera-Enriquez VP, et al. (2026) Ensembl 2026. Nucleic Acids Research 54(D1): D1053–D1060. doi:10.1093/nar/gkaf1239
- MANE transcripts
- Morales J, Pujar S, Loveland JE, Astashyn A, Bennett R, Berry A, et al. (2022) A joint NCBI and EMBL-EBI transcript set for clinical genomics and research. Nature 604(7905): 310–315. doi:10.1038/s41586-022-04558-8
- Pyodide (Python in the browser)
- The Pyodide development team. Pyodide: Python with the scientific stack, compiled to WebAssembly. pyodide.org
- NumPy
- Harris CR, Millman KJ, van der Walt SJ, Gommers R, Virtanen P, Cournapeau D, et al. (2020) Array programming with NumPy. Nature 585(7825): 357–362. doi:10.1038/s41586-020-2649-2
- tifffile, which gpxiltools reads and writes TIFF with
- Gohlke C. tifffile: read and write TIFF files. github.com/cgohlke/tifffile
- Pillow, which writes the PNG pictures
- Jeffrey A. Clark and contributors. Pillow, the friendly PIL fork. python-pillow.github.io
Privacy
GpxilStudio is a web page that does all its work in your browser. Your genomes, VCF files and everything you make aren’t uploaded anywhere, and there’s no server behind the page that could see them.
What stays on this computer
- Your files, notebooks and workflows, which are kept in this browser’s storage for the site (Your files). A file you add is read by the page and by Python in the page, and by nothing else.
- Small settings: the tab you were on, the help page you were reading, where you were in the Viewer, the commands you typed in the terminal, and the name you gave the terminal.
The page has no cookies, no accounts and no analytics.
What the page reads from the web
| When | From | What the server learns |
| Python first starts | cdn.jsdelivr.net (Pyodide, NumPy, Pillow, jsonschema and matplotlib) | nothing of yours: these are ordinary downloads, which your browser then keeps |
| The studio’s examples are read | the studio’s own web site | which parts of the example files are read |
The GRCh38 reference (/data/grch38) is read, by the Viewer, a tool or Python | the Gpxil browser’s site, pixelverse.cam | which chromosome is read, and which part of it: the pieces are fetched as they are needed, so the server sees the regions you work on, but nothing from your own files |
| gpxil2pxil looks up a gene without a GTF file of yours | the same site (the Gpxil browser’s gene files) | which genes you looked up |
curl, wget or pip install, in the terminal or a notebook | wherever you point them | whatever you send |
Both sites are served by GitHub Pages and jsDelivr, which keep ordinary logs of the requests they answer. If even the regions you look at must stay private, run your own copy of the studio with your own copy of the reference (On your own computer): then nothing is read from the web except Python, the first time.
Genomes are personal data
A gpxil file with a genotype plane is a person’s genome, and so is every faithful picture of it: a variants picture shows exactly where the person differs from the reference, and a book printed at one dot per base can be scanned and read back. A genome identifies the person, even without a name, and says things about their relatives, who haven’t agreed to anything (the gpxil specification, section 13). Use the studio as your institution’s rules on genetic data allow, and:
- Treat gpxil files, their pictures and books, and pxil pictures of a person’s haplotypes with the same care as the VCF or CRAM file they came from. Leaving out the reference plane makes a file smaller, not less personal: it’s the genotype planes that identify the person.
- Look at what a file says about the person before you share it. Its metadata keeps the sample’s ID and the names of the files it was made from (
gpxil info --json), and a pxil file names the sample and the haplotype.
- For pictures to show or publish, draw the reference only, or a region with nothing personal in it.
- On a shared computer, download your work and use My files → Start afresh when you finish.
The examples are public: HG002’s genome was sequenced and released, with consent, for anyone to use as a benchmark, which is why the studio can show it.
When something goes wrong
These are the common problems, and what to do about each one.
Python and memory
- “Python couldn’t start”
- The first time you use it, the studio downloads Python (about 15 MB) from cdn.jsdelivr.net. If your network blocks that site, or you’re offline, the tools, the notebook and the programs can’t run, although the Viewer and the Files tab still work, because they read gpxil files in the page itself. Try again on another network. After the first time, the browser usually keeps a copy.
- “Python stopped unexpectedly”, a
MemoryError, or the page reloads by itself
- Python ran out of memory. A browser tab has a few gigabytes at most, and some jobs need more: encoding holds about 6 bytes for each base of a chromosome, so chromosome 17 needs half a gigabyte and chromosome 1 about 1.5 GB, which may be too much; reading a whole chromosome’s planes at once in a notebook needs a byte a base for each plane. Reload the page: your files are kept. Then encode one chromosome at a time, read big regions in pieces, and do the biggest jobs on your own computer (A whole genome without running out of memory, Big files and memory, On your own computer).
- A tool waits for a long time
- A notebook cell may be running: the tools share the notebook’s Python, so they wait for it to finish. Stop the cell in the Notebook tab. The first tool of a visit also waits for Python to start, and a tool that reads the GRCh38 reference waits for its pieces to come from the web: encoding on chromosome 17 takes about a minute.
The reference
- A file in
/data/grch38 couldn’t be read
- The studio reads the GRCh38 reference from the Gpxil browser’s site, pixelverse.cam, a piece at a time. Check that you’re online and that your network doesn’t block the site. Your own files and the examples still work without it, and on your own computer you can run the studio with a copy of the reference (On your own computer).
- “contig chr17 is stored without its reference sequence”
- The file was written without its reference plane (
--no-ref), so anything that needs its bases needs the reference as well. In the Tools tab, fill in Its reference, which is filled in for you for a file made on GRCh38; in the terminal, add --reference /data/grch38/chr17.gpxil.tif; in Python, GpxilFile(path, reference=…). For a file made on another reference, give the FASTA file it was made from. The Viewer finds GRCh38 by itself.
- “… does not match the md5 of chr17”, or “… has length …, not …”
- The reference you gave isn’t the one the file was made on: another assembly, or another version of the chromosome. gpxil checks every reference against the checksums in the file, so that bases are never taken from the wrong one.
gpxil info names the reference the file was made on.
gpxil encode notes that REF alleles don’t match the reference
- The VCF was called against another reference, such as GRCh37 (hg19), so its variants are in the wrong places on this one. Encode it with the reference it was called against. A few mismatches can be harmless, such as variants in a VCF made from a different patch of the same assembly;
gpxil info --json lists the first of them for each contig.
- “VCF contigs not in the reference (skipped)”
- The reference has no sequence of that name. Each file in
/data/grch38 holds one chromosome, so a VCF of a whole genome encoded on chr17.gpxil.tif gives chromosome 17 only, and notes the others. Encode each chromosome with its own reference file, or use the whole-genome file on your own computer. Names with and without chr (17, chr17) are matched either way.
- Most of a chromosome isn’t called (grey in the Variants view, near-black in the Genotype view)
- That is what the file knows. With a BED file of the regions called with confidence, the bases outside them are UNKNOWN, because the VCF says nothing about them; Genome in a Bottle’s benchmark covers TP53 but not the whole of chromosome 17 (Turn someone’s VCF into a gpxil file).
Tools, pictures and genes
- A tool says the region is too big for the browser
- In the browser, a picture can show at most 25 million bases, and 64 million pixels; FASTA and VCF are written at most 50 million bases at a time; and the Viewer draws at most 4 million bases. Choose a smaller region, or fewer pixels per base. On your own computer, the same commands have no such limits.
- gpxil2pxil can’t find a gene
- “no gene … in the Gpxil browser’s genes”: check the gene’s symbol, or give its Ensembl ID (
ENSG…). “… is an alias of …”: give one of the genes it names. The Gpxil browser’s genes are Ensembl’s on GRCh38, so for a file made on another reference, give a GTF file of that reference’s genes (Genes from, or -a). A gene on a chromosome the file doesn’t have needs a file that has it, such as the chromosome’s file in /data/grch38.
- “… has no sample, so it has no haplotypes”
- The file holds only a reference, such as the files in
/data/grch38, so its pictures can only be of the reference: under From, choose the reference (--haplotypes ref).
- A picture, PDF or gpxil file made in the terminal won’t open
- It was written with
>, which is for text. Make it again with -o FILE (The Terminal).
- “… isn’t installed in this terminal”
- The terminal has the gpxil programs, Python, and the common text and file programs (
help lists them), but not bcftools, samtools, awk or gzip. The message says what to use instead. gpxil reads .vcf.gz and .fa.gz files as they are.
- A workflow can’t run, or one of its steps failed
- If the workflow can’t run, its Run page lists why, such as an input that isn’t connected. Open it in the editor, where Checks lists the same things and takes you to each step. If a step failed, open What went wrong under it: it shows the tool’s own error, the one you’d see if you ran the tool yourself, and the steps that read its files didn’t run. Put the step’s settings right in the editor, or choose another input file, and run it again (Workflows).
- A workflow’s run says stopped
- A workflow runs in this page, so closing or reloading the page stops it, and the steps that hadn’t run are marked stopped. The files of the steps that had finished are in the run’s folder. Run the workflow again.
Your files
- My files have gone
- Are you using the same browser, on the same computer, and not a private window? The browser keeps the files for this site only. If your browser clears site data when it closes, it deletes them too, so download anything you want to keep.
- “… is larger than 1 GB … it isn’t kept in the browser”
- A file that big is there while the page is open, but the browser won’t keep it. Download it before you leave. A gpxil file without its reference plane is a hundred times smaller.
- “The browser has no more room for the studio’s files”
- gpxil files are big. Download the files you want to keep, then delete those you no longer need from the Files tab. My files → Where are my files kept? shows how much room they take.
- “The studio is open in another tab or window too”
- Both tabs keep files in the same place, so one can overwrite what the other has saved. Close one of them.
Reporting a fault
If a tool fails and shows Python’s traceback (for reporting a fault) under the result, or a file the studio made doesn’t pass its own check, that’s a fault. Report it on the studio’s issues page, or on gpxiltools’ if it happens on your own computer too. Include the traceback and, if you can, a file that shows the problem: one of the examples, or a small made-up file such as the worked example. Never attach a person’s genome, or a picture of one.
Credits and licences
GpxilStudio is made by the Chronomic Medicine Group, Department of Genomic Medicine, University of Cambridge. It brings the gpxil toolkit together in one page, on the framework of the group’s Bioinformatics and Pedigree workbenches. It’s built from open-source software, open standards and public data, each under its own licence; THIRD_PARTY.md lists them with their versions.
Made by us
| Component | Licence |
| The gpxil and pxil specifications | MIT |
| gpxiltools, gpxil2pxil and pxil, the Python packages | MIT |
| The Gpxil browser, whose GRCh38 files and gene files the studio reads | MIT |
| gpxilwb, which runs the packages in the studio | MIT |
| The studio: the page, the Viewer, the help, the Tools tab and its workflows, the shell, the notebook and the editor (from the Bioinformatics and Pedigree workbenches) | MIT |
Made by others
| Component | Licence |
| tifffile (Christoph Gohlke), which gpxiltools reads and writes TIFF with, included in the studio’s archive of packages | BSD 3-clause |
| Pyodide (Python for the browser), with micropip, loaded from jsDelivr when Python starts | MPL 2.0 (Python itself: the PSF licence) |
| NumPy, Pillow and jsonschema (with attrs, referencing, rpds-py and jsonschema-specifications), loaded from jsDelivr when Python starts, and matplotlib, loaded once it has started | NumPy: BSD 3-clause; Pillow: MIT-CMU; jsonschema and its helpers: MIT; matplotlib: the matplotlib licence (PSF-based) |
| CodeMirror 5 (the editors) | MIT |
| marked (Markdown) | MIT |
| DOMPurify | Apache 2.0 or MPL 2.0 |
| JSZip | MIT or GPL 3 (used under MIT) |
Ideas, standards and data
- The genome pixel image is Guenter Albrecht-Buehler’s idea: a genome drawn as pictures, one pixel a base. gpxil takes it from one sequence to a person’s genome, with their genotype at every base, and the gpxi palette keeps his four greys.
- gpxil files are TIFF files, and they name their reference sequences by the GA4GH’s refget checksums. Variants come from VCF files, and bases are written in the IUPAC’s codes.
- The GRCh38 reference is the Genome Reference Consortium’s, as Ensembl release 116 gives it, with Ensembl’s genes and transcripts, and MANE’s choice of transcripts; the Gpxil browser made them into the files the studio reads.
- Chromosome 17’s repeats in
rmsk_chr17.txt.gz are RepeatMasker’s (Smit, Hubley and Green), as the UCSC Genome Browser’s rmsk table for hg38 has them: the UCSC Genome Browser’s sequence and annotation data are freely available for any use, crediting their contributors. Its protein-coding genes in chr17_genes.bed are Ensembl release 116’s.
- Pure GA-sequences, and the stacks that show the Alu elements before them, are Guenter Albrecht-Buehler’s discoveries, made in his genome pixel images.
- HG002’s genome in the examples is the Genome in a Bottle Consortium’s benchmark (NIST), version 4.2.1 and version 5.0q, for a man of the Personal Genome Project who agreed to its release, and HG003’s and HG004’s, his father’s and mother’s, are their version 4.2.1 benchmarks; the examples were made from them with gpxiltools. TP53’s transcripts in
TP53.gtf are Ensembl’s.
- The worked example is the gpxil specification’s.
Copyright © 2026 Chronomic Medicine Group, Department of Genomic Medicine, University of Cambridge.
References
If you use these programs in your work, cite the papers below, and give the versions you used (Versions, and how to cite):
- gpxil, the format
- gpxil continues Albrecht-Buehler’s genome pixel images (GPxI) as a file format.
- Chronomic Medicine Group, University of Cambridge (2026) gpxil: Genome Pixel Image format, version 0.1 (draft specification). github.com/comparativechrono/Gpxil
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- gpxiltools (the gpxil command, and the Python package)
- Chronomic Medicine Group, University of Cambridge (2026) gpxiltools: read, write, render and check gpxil files (Python). github.com/comparativechrono/Gpxil-tools
- gpxil2pxil
- Chronomic Medicine Group, University of Cambridge (2026) gpxil2pxil: pictures of genes, transcripts and proteins from a gpxil genome (Python). github.com/comparativechrono/gpxil2pxil
- pxil, the format and its Python module
- Chronomic Medicine Group, University of Cambridge (2026) pxil: sequence pixel files, version 0.1 (draft specification), and the pxil Python module. github.com/comparativechrono/pxil
- The Gpxil browser, and its GRCh38 files and gene files
- Chronomic Medicine Group, University of Cambridge (2026) The Gpxil browser: the GRCh38 genome as gpxil pictures, in a web browser. pixelverse.cam
- Genome pixel images (GPxI)
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- Pure GA-sequences, and their flanks
- The correction replaces the abstract’s “160 times fewer” point mutations with “many”.
- Albrecht-Buehler G (2008) Properties and distribution of pure GA-sequences of mammalian genomes. PLoS ONE 3(11): e3818. doi:10.1371/journal.pone.0003818
- Albrecht-Buehler G (2009) Correction: Properties and distribution of pure GA-sequences of mammalian genomes. PLoS ONE. doi:10.1371/annotation/7126ab55-ca2a-4202-9e66-fac899bd52ee
- Albrecht-Buehler G (2009) Outline of a genome navigation system based on the properties of GA-sequences and their flanks. PLoS ONE 4(3): e4701. doi:10.1371/journal.pone.0004701
- refget sequence identifiers
- Yates AD, Adams J, Chaturvedi S, Davies RM, Laird M, Leinonen R, et al. (2021) Refget: standardized access to reference sequences. Bioinformatics 38(1): 299–300. doi:10.1093/bioinformatics/btab524
- VCF, the variant call format
- Danecek P, Auton A, Abecasis G, Albers CA, Banks E, DePristo MA, et al. (2011) The variant call format and VCFtools. Bioinformatics 27(15): 2156–2158. doi:10.1093/bioinformatics/btr330
- GA4GH and the SAMtools team (hts-specs). The Variant Call Format specification, VCFv4.4. samtools.github.io/hts-specs/VCFv4.4.pdf
- IUPAC nucleotide codes
- Cornish-Bowden A (1985) Nomenclature for incompletely specified bases in nucleic acid sequences: recommendations 1984. Nucleic Acids Research 13(9): 3021–3030. doi:10.1093/nar/13.9.3021
- The GRCh38 reference genome
- Schneider VA, Graves-Lindsay T, Howe K, Bouk N, Chen HC, Kitts PA, et al. (2017) Evaluation of GRCh38 and de novo haploid genome assemblies demonstrates the enduring quality of the reference assembly. Genome Research 27(5): 849–864. doi:10.1101/gr.213611.116
- Ensembl (GRCh38 sequence, and genes from release 116)
- Yates AD, Austine-Orimoloye O, Azov AG, Barba M, Barnes I, Barrera-Enriquez VP, et al. (2026) Ensembl 2026. Nucleic Acids Research 54(D1): D1053–D1060. doi:10.1093/nar/gkaf1239
- MANE transcripts
- Morales J, Pujar S, Loveland JE, Astashyn A, Bennett R, Berry A, et al. (2022) A joint NCBI and EMBL-EBI transcript set for clinical genomics and research. Nature 604(7905): 310–315. doi:10.1038/s41586-022-04558-8
- Genome in a Bottle: HG002, his parents HG003 and HG004, and their benchmarks
- The examples HG002_chr17.gpxil.tif, HG002_TP53.vcf and HG002_TP53.bed come from HG002’s v4.2.1 benchmark, HG003_chr17.noref.gpxil.tif and HG004_chr17.noref.gpxil.tif from his parents’, and HG002_chr17_v5.0q.noref.gpxil.tif from his v5.0q benchmark, which is made from the T2T HG002 Q100 assembly of his genome (Hansen et al. 2026).
- Zook JM, Catoe D, McDaniel J, Vang L, Spies N, Sidow A, et al. (2016) Extensive sequencing of seven human genomes to characterize benchmark reference materials. Scientific Data 3: 160025. doi:10.1038/sdata.2016.25
- Wagner J, Olson ND, Harris L, Khan Z, Farek J, Mahmoud M, et al. (2022) Benchmarking challenging small variants with linked and long reads. Cell Genomics 2(5): 100128. doi:10.1016/j.xgen.2022.100128
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) small-variant benchmark v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/NISTv4.2.1/GRCh38
- Genome in a Bottle Consortium, NIST. HG003 (NA24149) and HG004 (NA24143), the parents of HG002: small-variant benchmarks v4.2.1, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio
- Hansen NF, Dwarshuis N, Ji HJ, Rhie A, Loucks H, Logsdon GA, et al. (2026) A complete diploid human genome benchmark for personalized genomics. Cell 189(16): 4857–4875.e31. doi:10.1016/j.cell.2026.06.016
- Genome in a Bottle Consortium, NIST. HG002 (NA24385) assembly-based small-variant benchmark v5.0q, GRCh38. ftp-trace.ncbi.nlm.nih.gov/ReferenceSamples/giab/release/AshkenazimTrio/HG002_NA24385_son/v5.0q
- RepeatMasker, and the UCSC Genome Browser’s table of its repeats on GRCh38 (rmsk_chr17.txt.gz)
- Smit AFA, Hubley R, Green P (2013–2015) RepeatMasker Open-4.0. www.repeatmasker.org
- Kent WJ, Sugnet CW, Furey TS, Roskin KM, Pringle TH, Zahler AM, et al. (2002) The human genome browser at UCSC. Genome Research 12(6): 996–1006. doi:10.1101/gr.229102
- UCSC Genome Browser. RepeatMasker’s repeats on hg38 (GRCh38): the rmsk table. hgdownload.soe.ucsc.edu/goldenPath/hg38/database
- Pyodide (Python in the browser)
- The Pyodide development team. Pyodide: Python with the scientific stack, compiled to WebAssembly. pyodide.org
- NumPy
- Harris CR, Millman KJ, van der Walt SJ, Gommers R, Virtanen P, Cournapeau D, et al. (2020) Array programming with NumPy. Nature 585(7825): 357–362. doi:10.1038/s41586-020-2649-2
- tifffile, which gpxiltools reads and writes TIFF with
- Gohlke C. tifffile: read and write TIFF files. github.com/cgohlke/tifffile
- Pillow, which writes the PNG pictures
- Jeffrey A. Clark and contributors. Pillow, the friendly PIL fork. python-pillow.github.io