15 Variant Annotation and Interpretation: Implementations
Everything on this page is generated from tools/annotation.json. If something here is wrong or out of date, the fix belongs in that file, not in this prose.
For why a consequence is a claim about a transcript rather than about a variant, and why the databases matter more than the programs, see Section 14.1.
15.1 Start here
Annotation is two jobs that get called one word. Deriving a consequence computes something new from the variant, the reference and a gene model — Section 14.2. Transferring attaches a fact somebody else established: a population frequency, a clinical assertion, a precomputed score. A pipeline needs both, they fail in different ways, and the tools below are not interchangeable between them — one of them does both, which is convenient and not the same as the distinction going away.
What samtools is to alignments, this is to variants: reading, filtering, annotating, merging and subsetting VCF and BCF, released in lockstep with samtools and htslib. Its filtering expression language is the part worth learning first, because it replaces a great deal of fragile text processing. Its csq subcommand is also the rare consequence caller that reads phase, so two variants sharing a codon are annotated as the haplotype they actually form rather than twice, independently, as though each were alone. (Danecek et al. 2021, 2011; Danecek and McCarthy 2017)
The reference implementation of the consequence vocabulary the rest of the field writes its filters against, released in step with the Ensembl gene set it annotates from. Its plugin interface is the reason to start here rather than the consequence calls themselves: the score sets you will want next — splice models, precomputed missense predictors, loss-of-function confidence, aggregate frequency databases — attach to the same pass instead of becoming a second annotation stage with its own coordinate conventions to get wrong. (McLaren et al. 2016, 2010)
The other half of annotation, and the half a consequence engine does not do: attaching fields from your own tabixed VCF, BED and GFF sources onto a callset. Any number of sources are declared in one TOML file and applied in a single parallel pass, which matters less for the speed than for the file: that config is a checkable record of exactly which database versions produced the INFO fields a downstream filter then keys on. (Pedersen et al. 2016)
15.1.1 The other engine
One alternative worth knowing, and it is not a weaker version of the tools above — it answers a question they answer badly. If your organism is not one of the handful with a curated, versioned annotation set, the problem stops being which consequence caller to run and becomes where an annotation database comes from at all.
| Tool | Upstream | Languages | Why / why not |
|---|---|---|---|
| SnpEff | activelast release 2026-02 | java |
A single jar with pre-built databases for a large number of genomes and a |
15.2 Predicting what nobody has measured
Nothing in this section is recommended, and that is the finding. The verdicts here are all viable, because a predictor is not a tool you choose once and standardize on — it is evidence of a stated strength, admissible for some claims and not others (Section 14.4).
The practical shape is also different from every other page in this book. For each of these, the thing most readers consume is a precomputed table joined onto a callset, not a program executed on one. The record is here because the model, its license and its release history are what you are actually depending on, and a table hides all three.
| Tool | Upstream | Languages | Why / why not |
|---|---|---|---|
| CADD | activelast release 2025-10 | pysh |
One score for every position in the genome, coding and non-coding alike, which is what makes it useful as a ranking prior when you have no other handle on a non-coding candidate. Its label is not curated pathogenicity but a proxy — variants that survived in the human lineage against simulated ones that did not — so it is a measure of depletion, not of disease. Use it to order a list; do not enter it as evidence. (Kircher et al. 2014; Schubach et al. 2024) |
| Pangolin | dormantlast release 2022-03 | py |
Predicts the change in splice-site usage rather than the presence of a site, and is trained across several tissues and species, so it answers a quantitative question where SpliceAI answers a categorical one. It is also GPL-3.0, which is the practical reason to reach for it: SpliceAI’s non-commercial terms rule that tool out for a large class of users, and this is the closest thing to a drop-in that they do not. (Zeng and Li 2022) |
| SpliceAI | deprecatedlast release 2020-03 | py |
Predicts splice-altering effects from sequence alone, and is the model the field’s splicing evidence is anchored on — which is the reason to use it and also the reason to treat it as a fixed reference rather than a dependency. The repository is archived, the code is under a license that permits neither modification nor redistribution, and the models are academic and non-commercial only. Consume it as a score source, through the precomputed set or a VEP plugin, and do not plan on the code moving. (Jaganathan et al. 2019) |
SpliceAI’s code forbids modification and redistribution and its models are academic and non-commercial; CADD’s license runs to non-commercial users. This is not a detail to resolve later — it decides whether a score can appear in a commercial report, a redistributable container, or a public reanalysis, and it does not travel with the score once the score is a column in a VCF. Pangolin is GPL-3.0 and is in this book substantially for that reason.
15.3 From a callset to a shortlist
Annotation produces a file with more columns; it does not produce an answer. What survives every filter that can be applied without knowing what you are looking for is still far more than anyone can read — Section 14.5. The tools here bring in the thing that does know: a pedigree, or a phenotype.
Ranks candidate genes against the patient’s phenotype rather than filtering variants against thresholds, which is the question a rare-disease case actually poses. Frequency, predicted effect, inheritance model and HPO-term similarity — including similarity to model-organism phenotypes, for genes with no human disease association yet — are combined into one ordering. The analysis is one YAML file against a dated data release, so a re-run a year later is reproducible or visibly not. (Smedley et al. 2015)
Expresses an inheritance model — de novo, recessive, compound heterozygote — as a short JavaScript expression evaluated against a pedigree and the genotype fields, so the filter that produced a candidate list is a line you can paste into a methods section instead of a script nobody will read again. The paper it comes from also publishes the candidate counts each model should yield, which is the number that tells you a filter is working before you go looking at genes. (Pedersen et al. 2021)
| Tool | Upstream | Languages | Why / why not |
|---|---|---|---|
| InterVar | dormantlast release 2021-08 | py |
The first serious attempt to mechanize the ACMG-AMP framework, and still the clearest statement of how much of it can be mechanized: 18 of the 28 criteria, with the remaining ten left for a human because they need evidence a program does not have. That accounting is worth knowing. The output is a starting classification for review, and a pipeline that reports it as a classification has misread the tool rather than misused it. (Li and Wang 2017) |
The ACMG-AMP framework (Richards et al. 2015) is a procedure for weighing evidence by people, not an algorithm. Its own authors say so. A program can assign the criteria that reduce to a database lookup and it cannot assign the ones that require judgment about a specific gene and disease, which is why the tool above that tries reports on 18 of 28 and stops. Software that emits “Pathogenic” as a value has changed what the word means.
15.4 Structural variants
The variant calling part deferred interpretation to this page, and structural variation deferred it twice over, because a rearrangement breaks the model the engines above are built on: there is no single position, no reference and alternate allele to compare, and the consequence depends on how much of a gene the event covers rather than on which codon it lands in (Section 14.6). The tools are correspondingly different, and a small-variant annotation pipeline will not do this job by accident.
A structural variant is annotated by what it overlaps, so the work is a join against gene models, population frequency sets, dosage sensitivity, regulatory elements and disease databases — and the value here is that the join is assembled, versioned and installed for you rather than rebuilt per project. It reports both a whole-event line and one line per gene the event touches, which is the split the question needs: one deletion is a single variant and several separate clinical arguments. (Geoffroy et al. 2018)
| Tool | Upstream | Languages | Why / why not |
|---|---|---|---|
| duphold | dormantlast release 2020-12 | nim |
Adds depth fold-change fields to an SV callset so a deletion can be checked against whether coverage actually fell there. The evidence is orthogonal to what a breakpoint caller used to make the call in the first place, which is what makes it worth a pass: read-pair and split-read support agreeing with each other says less than either agreeing with depth. (Pedersen and Quinlan 2019) |
| SvAnna | activelast release 2025-11 | java |
Phenotype-driven prioritization for structural variants — the Exomiser argument applied to rearrangements, from the same group — and it scores events that disrupt regulatory sequence rather than only those that hit a coding exon. Reach for it when the case has HPO terms and a long-read callset, which is the setting it was built and evaluated for. (Danis et al. 2022) |
15.5 What is not here
ANNOVAR (Wang et al. 2010) is the conspicuous absence. It is one of the three annotators you will meet most often, it is still in production everywhere, and it carries no record in this registry — not because the book has no view of it, but because the book cannot make its usual promise about it. Every record here says “reviewed YYYY-MM” next to a version read automatically from an upstream feed. ANNOVAR has no public source repository, its download is registration-walled, and its releases are dated strings on a web page. A record for it would either carry an empty version field, which reads as “nobody looked”, or a hand-typed one, which is the exact failure mode the refresh script exists to prevent. So the position goes here instead, in prose that can be honest about its own footing: it works, you will inherit pipelines built on it, its database collection is genuinely broad — and for a new pipeline the open engines above give you the same annotations under terms you can redistribute and a release history you can watch.
The databases are not surveyed as tools. gnomAD, ClinVar, dbNSFP, REVEL, AlphaMissense, ClinGen’s dosage-sensitivity map: these are the substance of an annotation, and choosing them is the methods decision (Section 14.3). But they are versioned datasets rather than software, they are consumed through the tools above rather than run, and a survey of them would need a review cadence this book does not have. The ones that carry an argument here are named in the concepts chapter, with citations.
Somatic annotation and reporting. Oncogenicity classification follows a different framework from ACMG-AMP for germline variants, tumor-suppressor and oncogene evidence is asymmetric in ways the tools above do not model, and the output is a clinical report rather than a filtered VCF. Same reason somatic calling is absent from the variant calling part: a different question, not a harder version of this one.
Commercial interpretation platforms. Several exist, some are very widely used in diagnostic laboratories, and none can be surveyed the way this book surveys anything else — there is no repository to read, no license file to check, and no release feed. A page that ranked them would be recycling marketing copy under a review date.
15.6 Language coverage
This part is the most linguistically scattered in the book, and the reason is that no two of these tools are doing the same kind of work.
The consequence engines are Perl and Java — languages chosen when they were written, kept because a consequence caller’s cost is dominated by the database join and not by the inner loop. Nothing here is compiled for speed the way an aligner is, and nothing needs to be.
The tools that are fast are fast because they are doing set operations at genome scale: vcfanno in Go, slivar and duphold in Nim. That is a small, consistent tradition — Brent Pedersen wrote all three of those, plus smoove in the structural variation part — and it is the clearest example in this book of a modern compiled language being adopted for bioinformatics tooling and sticking.
The predictors are Python, because they are neural networks and that is where the frameworks are. And AnnotSV is Tcl, which is the only appearance of that language in this book and needs no defense: it is a data-joining pipeline, it has been maintained continuously for years, and the language it is written in is not something a user of it ever has to touch.
As everywhere else in this book, you drive these from the command line and read the result back in. The difference here is what “the result” is: an annotated VCF whose INFO fields are named by your configuration, not by a standard. A downstream script that hard-codes gnomAD_AF is coupled to the annotation run that produced it. Keep the vcfanno config, or the VEP command, next to the analysis that consumes its output — Section 22.6 is about exactly this.