orthoRibbon Vignette: Ribbon-Diagram Visualization of Orthologous Genes Across Species

Introduction

Comparative genomics plays a central role in understanding gene conservation and evolutionary relationships across species. Orthologous genes, which arise from speciation events and often retain similar biological functions, provide a fundamental framework for cross-species genomic comparison. Visualizing these relationships in an intuitive and scalable manner is important for interpreting genome organization and gene correspondence among multiple species.

OrthoRibbon is an R package designed to visualize orthologous gene relationships across species using ribbon-based diagrams. The package connects homologous genes between genomes through ribbons that link their genomic positions, enabling users to easily explore conserved genes and cross-species genomic structures. By transforming ortholog tables into clear visual representations, OrthoRibbon facilitates comparative genomic analysis and produces customizable, publication-quality figures.

OrthoRibbon supports flexible input formats and can integrate ortholog annotations from common databases such as Ensembl or other orthology inference tools. The package is designed to be lightweight and highly customizable, allowing users to control genomic layouts, ribbon aesthetics, and species ordering. These features make OrthoRibbon a practical visualization tool for researchers studying evolutionary conservation, genome organization, and cross-species gene relationships.

Installation

if (!require("BiocManager", quietly = TRUE)) {
    install.packages("BiocManager")
}
BiocManager::install("orthoRibbon")

Quick start

There are six main steps to visualize your data with orthoRibbon:

Step 1. Load the required library.

library(orthoRibbon)
library(geneClusterPattern)
library(GenomeInfoDb)

Step 2. Define the species abbreviation. They are abbreviated species identifiers derived from the binomial (Latin) species names. You can try to guess it from common names. The plot order is decided by the order of your input show as ‘common_name’ here.

common_name <- geneClusterPattern::guessSpecies(
  c('human', 'fruit fly'))
names(common_name) <- common_name # must assign the names.
common_name
##        hsapiens   dmelanogaster 
##      "hsapiens" "dmelanogaster"

Step 3. Retrieve chromosome, homologs, and gene coordinates. You can get the ortholog data via tools like OrthoFinder, odp, or databases like OrthoDB, eggNOG.

extdata <- system.file('extdata', package='orthoRibbon')
chrom_infos <- readRDS(file.path(extdata, 'human_fly/chrom_infos.rds'))
names(chrom_infos)
## [1] "hsapiens"      "dmelanogaster"
head(chrom_infos[[1]], n=2)
##         name length coord_system     synonyms toplevel non_ref circular
## 1 KI270510.1   2415     scaffold chrUn_KI....     TRUE   FALSE       NA
## 2 KI270539.1    993     scaffold chrUn_KI....     TRUE   FALSE       NA
## the homologs_df must contain columns 'gene_id', and 'ortholog_group'.
homologs_df <- readRDS(file.path(extdata, 'human_fly/homologs_df.rds'))
head(homologs_df, n=2)
##           gene_id ortholog_group symbol  species seq    start
## 1 ENSG00000028277              1 POU2F2 hsapiens  19 42086110
## 2 ENSG00000064835              1 POU1F1 hsapiens   3 87259404

Step 4. Prepare the plot data.

If the homologs_df data frame already contains gene location information (specifically, the “species”, “seq”, and “start” columns), the genes_gr argument can be omitted. Otherwise, gene annotations must be provided via the genes_gr argument as a GRanges object, with “gene_name” and “species” stored as metadata columns.

plotData <- buildPlotData(common_name, homologs_df,
                          chrom_infos = chrom_infos,
                          chromosome_order_method = 'max',
                          max_links=1000)
names(plotData)
## [1] "homolog_df_list"  "chrom_bars_df"    "chrom_label_df"   "symbol_df_top"   
## [5] "symbol_df_bottom" "common_name"
# Here subset the plotData to reduce the vignette file size
# This is not necessary for real plot.
plotData <- subset(plotData,
                   subset=list('hsapiens'=c('1', '2', '3', 'X'),
                               'dmelanogaster'=c('2L', '2R', 'X')))

Step 5. Calculate the exactly position for Bezier curve.

There are three methods for calculating gene positions: chromosome coordinates, gene index, and homologous chromosome. Here we show the example by homologous chromosome and will discuss the details later.

# by homolog chromosomes
bezier_mini <- buildBezierDF(plotData$homolog_df_list,
                             colname1='topMini_finalOffset',
                             colname2='bottomMini_finalOffset')

Step 6. Plot the data.

orthoRibbonPlot(common_name, bezier_mini,
                plotData$chrom_bars_df,
                plotData$chrom_label_df)

Details

Input data

OrthoRibbon supports flexible input formats and can integrate ortholog annotations from a variety of sources, including commonly used databases such as Ensembl and results generated by other orthology inference tools. In the quick start section, the data is the output of orthoFinder for human and fruit fly, which is based on ortholog groups information. OrthoRibbon can be readily adapted to using the homolog pairs like data retrieved from Ensembl database.

Here is sample code to prepare the data by retrieving chromosome, homologs and gene coordinates via BioMart.

# prepare mart for BioMart
marts <- geneClusterPattern::guessSpecies(
  common_name, output='mart',
  host='https://jun2026.archive.ensembl.org')

# prepare all the Ensembl ids
ids <- orthoRibbon::getGeneIDs(common_name, marts)

# get chromosome info
chrom_infos<- lapply(common_name, GenomeInfoDb::getChromInfoFromEnsembl)

# retrieve all the homologs
homologs <- orthoRibbon::getHomologGRs(ids, common_name, marts)

# retrieve all the gene ranges
genes_gr <- orthoRibbon::getGeneGRs(ids, marts, homologs)

# reformat the homologs to data.frame
homologs_df <- getHomologIDs(homologs)

Since this step retrieve information via “biomaRt”, it will take some time or out of service.

We will load presaved data.

common_name <- c("hsapiens", "mmusculus", "drerio")
common_name <- setNames(common_name, common_name)
extdata <- system.file('extdata', 'ensembl', package='orthoRibbon')
chrom_infos <- readRDS(file.path(extdata, 'chrom_infos.rds'))
names(chrom_infos)
## [1] "hsapiens"  "mmusculus" "drerio"
head(chrom_infos[[1]], n=2)
##     name   length coord_system     synonyms toplevel non_ref circular
## 474   19 58617616   chromosome CM000681....     TRUE   FALSE       NA
## 475   20 64444167   chromosome CM000682....     TRUE   FALSE       NA
homologs_df <- readRDS(file.path(extdata, 'homolog_df.human.mouse.zebrafish.rds'))
head(homologs_df, n=2)
##          gene_id1           gene_id2
## 2 ENSG00000091583 ENSMUSG00000000049
## 7 ENSG00000146535 ENSMUSG00000000149
genes_gr <- readRDS(file.path(extdata, 'gene.GRanges.obj.rds'))
head(genes_gr, n=2)
## GRanges object with 2 ranges and 2 metadata columns:
##                      seqnames              ranges strand |   gene_name
##                         <Rle>           <IRanges>  <Rle> | <character>
##   ENSMUSG00000000049       11 108234180-108305222      + |        Apoh
##   ENSMUSG00000000149        5 140744163-140816186      - |       Gna12
##                          species
##                      <character>
##   ENSMUSG00000000049   mmusculus
##   ENSMUSG00000000149   mmusculus
##   -------
##   seqinfo: 1489 sequences from an unspecified genome; no seqlengths

Set chromosome order

Setting an appropriate chromosome order can make the data presentation much cleaner by reducing the number of crossing curves and making the overall pattern easier to interpret.

OrthoRibbon provides seven different methods for arranging chromosome order:

  1. Max: minimizes the chromosome distance.
  2. GW (Gruvaeus–Wainer): minimizes the gradient measure.
  3. OLO (Optimal Leaf Ordering): optimizes the ordering of leaves in a dendrogram.
  4. TSP (Traveling Salesman Problem): minimizes the total path length.
  5. Spectral: determines the order using eigenvectors.
  6. Spearman distance: uses Spearman distance to determine chromosome similarity and ordering.
  7. Customized: allows users to specify their own chromosome order.

You may want to try different ordering methods and compare the resulting visualizations to determine which one provides the clearest representation of your data.

# Different methods optimize different criteria:
# 1. max - minimizes the chromosome distance 
chr_order_max <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
                          chromosome_order_method = "max")
# 2. GW (Gruvaeus-Wainer) - minimizes gradient measure
chr_order_gw <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
                          chromosome_order_method = "GW")

# 3. OLO (Optimal Leaf Ordering) - optimal for dendrograms
chr_order_olo <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
                          chromosome_order_method = "OLO")

# 4. TSP (Traveling Salesman Problem) - minimizes path length
chr_order_tsp <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
                          chromosome_order_method = "TSP")

# 5. Spectral - uses eigenvectors
chr_order_spec <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
                          chromosome_order_method = "Spectral")

# 6. Spearman distance
chr_order_dist <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
                          chromosome_order_method = "spearman")

# 7. customized
seq_names <- lapply(chrom_infos, function(.ele) sortSeqlevels(.ele$name))
chr_order_manu <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
                                chr_orders=seq_names)

Methods for the position of Bezier curve.

As describe above, there are three methods for calculating gene positions: chromosome coordinates, gene index, and homologous chromosome.

  • Chromosome coordinates: Positions are calculated based on the genomic coordinates of the genes. This method emphasizes the actual physical locations and distances between genes on the chromosomes.

  • Gene index: Positions are based on the rank of each gene along the chromosome, ordered by genomic coordinates. This method emphasizes the organization of homologous gene clusters while ignoring gene lengths and the gaps between genes.

  • Homologous chromosome: Positions are summarized by the proportion of homologous genes shared between each pair of chromosomes. This method emphasizes the relative proportion of homologous genes between chromosome pairs.

# chr_order_max as plotData from last step
# Where are the homologs of mouse X-linked genes located in the zebrafish and human genomes?
plotData <- subset(chr_order_max, subset=list('mmusculus'=c('X')))
# by coordinates
bezier_chr <- buildBezierDF(plotData$homolog_df_list,
                           colname1='topChr_finalOffset',
                           colname2='bottomChr_finalOffset')
# by the index
bezier_ix <- buildBezierDF(plotData$homolog_df_list,
                           colname1='topIx_finalOffset',
                           colname2='bottomIx_finalOffset')
# by homolog chromosome names
bezier_mini <- buildBezierDF(plotData$homolog_df_list,
                             colname1='topMini_finalOffset',
                             colname2='bottomMini_finalOffset')

Plot the data generated by each method to compare their results.

# by coordinates
orthoRibbonPlot(common_name, bezier_chr,
                plotData$chrom_bars_df,
                plotData$chrom_label_df)

# # by the index
# orthoRibbonPlot(common_name, bezier_ix,
#                 plotData$chrom_bars_df,
#                 plotData$chrom_label_df)
# by homologous chromosome names
# the gene is not ordered by coordinates but ordered by the homologous chromosome names
## And Here we will show the symbols for each homologous.
symbol_df_top <- plotData$symbol_df_top
symbol_df_top <- 
  symbol_df_top[sample(seq.int(nrow(symbol_df_top)), 50), ] # random select 50 symbols
symbol_df_top$x <- symbol_df_top$xMini
symbol_df_bottom <- plotData$symbol_df_bottom
symbol_df_bottom <- 
  symbol_df_bottom[sample(seq.int(nrow(symbol_df_bottom)), 50), ] # random select 50 symbols
symbol_df_bottom$x <- symbol_df_bottom$xMini
orthoRibbonPlot(common_name, bezier_mini,
                plotData$chrom_bars_df,
                plotData$chrom_label_df,
                show_symbol=TRUE,
                symbol_df_top=symbol_df_top,
                symbol_df_bottom=symbol_df_bottom,
                symbol_size=3,
                link_alpha=0.1) # add alpha value for the link

SessionInfo

## R version 4.6.1 (2026-06-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 26.04.1 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Etc/UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
## [1] GenomeInfoDb_1.49.1       Seqinfo_1.3.2            
## [3] IRanges_2.47.5            S4Vectors_0.51.10        
## [5] BiocGenerics_0.59.12      generics_0.1.4           
## [7] geneClusterPattern_0.99.2 orthoRibbon_0.99.0       
## 
## loaded via a namespace (and not attached):
##   [1] DBI_1.3.0                   bitops_1.1-0               
##   [3] httr2_1.3.0                 biomaRt_2.69.4             
##   [5] rlang_1.3.0                 magrittr_2.0.5             
##   [7] otel_0.2.0                  matrixStats_1.5.0          
##   [9] compiler_4.6.1              RSQLite_3.53.3             
##  [11] GenomicFeatures_1.65.0      png_0.1-9                  
##  [13] vctrs_0.7.3                 txdbmaker_1.9.0            
##  [15] stringr_1.6.0               pwalign_1.9.1              
##  [17] pkgconfig_2.0.3             crayon_1.5.3               
##  [19] fastmap_1.2.0               dbplyr_2.6.0               
##  [21] XVector_0.53.0              labeling_0.4.3             
##  [23] ca_0.72                     trackViewer_1.49.3         
##  [25] Rsamtools_2.29.0            rmarkdown_2.32             
##  [27] UCSC.utils_1.9.0            strawr_0.0.92              
##  [29] bit_4.6.0                   xfun_0.61                  
##  [31] cachem_1.1.0                cigarillo_1.3.1            
##  [33] jsonlite_2.0.0              progress_1.2.3             
##  [35] blob_1.3.0                  rhdf5filters_1.25.4        
##  [37] DelayedArray_0.39.8         tweenr_2.0.3               
##  [39] Rhdf5lib_2.1.0              BiocParallel_1.47.0        
##  [41] parallel_4.6.1              prettyunits_1.2.0          
##  [43] R6_2.6.1                    bslib_0.12.0               
##  [45] stringi_1.8.9               RColorBrewer_1.1-3         
##  [47] rtracklayer_1.73.0          GenomicRanges_1.65.4       
##  [49] jquerylib_0.1.4             iterators_1.0.14           
##  [51] Rcpp_1.1.2                  SummarizedExperiment_1.43.0
##  [53] knitr_1.52                  BiocBaseUtils_1.15.1       
##  [55] Matrix_1.7-6                tidyselect_1.2.1           
##  [57] abind_1.4-8                 yaml_2.3.12                
##  [59] TSP_1.2.7                   codetools_0.2-20           
##  [61] curl_8.0.0                  lattice_0.23-1             
##  [63] tibble_3.3.1                withr_3.0.3                
##  [65] S7_0.2.2                    InteractionSet_1.41.0      
##  [67] Biobase_2.73.2              KEGGREST_1.53.6            
##  [69] evaluate_1.0.5              polyclip_1.10-7            
##  [71] BiocFileCache_3.3.0         Biostrings_2.81.9          
##  [73] pillar_1.11.1               BiocManager_1.30.27        
##  [75] filelock_1.0.3              MatrixGenerics_1.25.0      
##  [77] foreach_1.5.2               RCurl_1.98-1.20            
##  [79] ggplot2_4.0.3               hms_1.1.4                  
##  [81] scales_1.4.0                BiocStyle_2.41.0           
##  [83] grImport_0.9-7              glue_1.8.1                 
##  [85] maketools_1.3.2             tools_4.6.1                
##  [87] BiocIO_1.23.3               sys_3.4.3                  
##  [89] GenomicAlignments_1.49.2    registry_0.5-1             
##  [91] RANN_2.6.3                  buildtools_1.0.0           
##  [93] XML_3.99-0.25               rhdf5_2.57.18              
##  [95] grid_4.6.1                  seriation_1.5.8            
##  [97] AnnotationDbi_1.75.2        ggforce_0.5.0              
##  [99] restfulr_0.0.17             cli_3.6.6                  
## [101] S4Arrays_1.13.2             dplyr_1.2.1                
## [103] gtable_0.3.6                sass_0.4.10                
## [105] digest_0.6.39               ggrepel_0.9.8              
## [107] SparseArray_1.13.4          rjson_0.2.23               
## [109] htmlwidgets_1.6.4           farver_2.1.2               
## [111] memoise_2.0.1               htmltools_0.5.9            
## [113] lifecycle_1.0.5             httr_1.4.9                 
## [115] MASS_7.3-66                 bit64_4.8.6