# Visualizing GEXF graphs with sigma.js

#### George G. Vega Yon

#### 2026-07-22

# Overview

`rgexf` ships with an interactive graph viewer built on top of sigma.js v3 and graphology. The widget is zero-dependency from the user’s perspective: the required JavaScript libraries are bundled inside the package, so no internet connection is needed at runtime.

The main entry point is `plot()` — the standard R S3 method dispatches to the sigma.js widget automatically for `gexf` objects. The underlying `sigmajs()` function is also exported for cases where you need extra arguments such as `borderColor` or explicit `width`/`height`.

# 1 A simple coloured graph

## 1.1 Creating the graph

We start by building a small three-node graph from scratch. `rgexf` stores visual properties (colour, position, size) through the `nodesVizAtt` argument of `gexf()`.

    library(rgexf)

    # Node and edge tables
    nodes <- data.frame(
      id    = 1:3,
      label = c("Alice", "Bob", "Carol")
    )
    edges <- data.frame(
      source = c(1L, 2L, 1L),
      target = c(2L, 3L, 3L)
    )

    # Visual attributes
    node_colors <- data.frame(
      r = c(220L,  66L,  40L),
      g = c( 50L, 133L, 167L),
      b = c( 47L, 244L, 240L),
      a = c(  1,    1,    1)
    )
    node_positions <- data.frame(
      x = c(-1,  1,  0),
      y = c( 0,  0,  1.5),
      z = c( 0,  0,  0)
    )
    node_sizes <- c(8, 8, 8)

    g <- gexf(
      nodes        = nodes,
      edges        = edges,
      nodesVizAtt  = list(
        color    = node_colors,
        position = node_positions,
        size     = node_sizes
      )
    )

## 1.2 Plotting the graph

Calling `plot()` on a `gexf` object renders the sigma.js widget directly.

    plot(g)

By default sigma.js renders nodes as plain filled circles — the node’s `color` fills the entire disc. You can add an optional border ring by passing `borderColor` (any extra arguments are forwarded to `sigmajs()`):

    # White border ring (15 % of the node radius)
    plot(g, borderColor = "#ffffff", borderSize = 0.15)

    # Dark charcoal border, slightly thicker
    plot(g, borderColor = "#333333", borderSize = 0.2)

# 2 The Les Misérables network

The package includes a classic co-occurrence network from Victor Hugo’s Les Misérables, originally prepared for Gephi. It already contains `viz:color`, `viz:position`, and `viz:size` attributes, so the widget picks them up automatically.

## 2.1 Reading the file

    lesmi_path <- system.file("gexf-graphs/lesmiserables.gexf", package = "rgexf")
    lesmi      <- read.gexf(lesmi_path)
    lesmi

    #> <?xml version="1.0" encoding="UTF-8"?>
    #> <gexf xmlns="http://www.gexf.net/1.3" xmlns:viz="http://www.gexf.net/1.3/viz" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.3" xsi:schemaLocation="http://www.gexf.net/1.3 http://www.gexf.net/1.3/gexf.xsd">
    #>   <meta lastmodifieddate="2016-11-09">
    #>     <creator>Gephi 0.9</creator>
    #>     <description/>
    #>   </meta>
    #>   <graph defaultedgetype="undirected" mode="static">
    #>     <attributes class="node" mode="static">
    #>       <attribute id="modularity_class" title="Modularity Class" type="integer"/>
    #>     </attributes>
    #>     <nodes>
    #>       <node id="11" label="Valjean">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="1"/>
    #>         </attvalues>
    #>         <viz:size value="100.0"/>
    #>         <viz:position x="-87.93029" y="6.8120565"/>
    #>         <viz:color r="245" g="91" b="91"/>
    #>       </node>
    #>       <node id="48" label="Gavroche">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="8"/>
    #>         </attvalues>
    #>         <viz:size value="61.600006"/>
    #>         <viz:position x="387.89572" y="-110.462326"/>
    #>         <viz:color r="91" g="245" b="91"/>
    #>       </node>
    #>       <node id="55" label="Marius">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="6"/>
    #>         </attvalues>
    #>         <viz:size value="53.37143"/>
    #>         <viz:position x="206.44687" y="13.805411"/>
    #>         <viz:color r="194" g="91" b="245"/>
    #>       </node>
    #>       <node id="27" label="Javert">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="7"/>
    #>         </attvalues>
    #>         <viz:size value="47.88571"/>
    #>         <viz:position x="-81.46074" y="204.20204"/>
    #>         <viz:color r="91" g="245" b="194"/>
    #>       </node>
    #>       <node id="25" label="Thenardier">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="7"/>
    #>         </attvalues>
    #>         <viz:size value="45.142853"/>
    #>         <viz:position x="82.80825" y="203.1144"/>
    #>         <viz:color r="91" g="245" b="194"/>
    #>       </node>
    #>       <node id="23" label="Fantine">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="2"/>
    #>         </attvalues>
    #>         <viz:size value="42.4"/>
    #>         <viz:position x="-313.42786" y="289.44803"/>
    #>         <viz:color r="91" g="194" b="245"/>
    #>       </node>
    #>       <node id="58" label="Enjolras">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="8"/>
    #>         </attvalues>
    #>         <viz:size value="42.4"/>
    #>         <viz:position x="355.78366" y="74.882454"/>
    #>         <viz:color r="91" g="245" b="91"/>
    #>       </node>
    #>       <node id="62" label="Courfeyrac">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="8"/>
    #>         </attvalues>
    #>         <viz:size value="36.91429"/>
    #>         <viz:position x="436.17184" y="12.7286825"/>
    #>         <viz:color r="91" g="245" b="91"/>
    #>       </node>
    #>       <node id="64" label="Bossuet">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="8"/>
    #>         </attvalues>
    #>         <viz:size value="36.91429"/>
    #>         <viz:position x="455.81955" y="115.45826"/>
    #>         <viz:color r="91" g="245" b="91"/>
    #>       </node>
    #>       <node id="63" label="Bahorel">
    #>         <attvalues>
    #>           <attvalue for="modularity_class" value="8"/>
    #>         </attvalues>
    #>         <viz:size value="34.17143"/>
    #>         <viz:position x="602.55225" y="-16.421427"/>
    #>         <viz:color r="91" g="245" b="91"/>
    #>       </node>
    #>          ...
    #>      </nodes>
    #>     <edges>
    #>       <edge id="0" source="1" target="0"/>
    #>       <edge id="1" source="2" target="0" weight="8.0"/>
    #>       <edge id="2" source="3" target="0" weight="10.0"/>
    #>       <edge id="3" source="3" target="2" weight="6.0"/>
    #>       <edge id="4" source="4" target="0"/>
    #>       <edge id="5" source="5" target="0"/>
    #>       <edge id="6" source="6" target="0"/>
    #>       <edge id="7" source="7" target="0"/>
    #>       <edge id="8" source="8" target="0" weight="2.0"/>
    #>       <edge id="9" source="9" target="0"/>
    #>          ...
    #>      </edges>
    #>   </graph>
    #> </gexf>

## 2.2 Default plot

    plot(lesmi)

The colours come directly from the GEXF file. Each character group is assigned a distinct hue by the original dataset, so the community structure is immediately visible.

## 2.3 Adding node borders for contrast

The default rendering is sigma.js’s plain filled circle. If you want to separate overlapping nodes or simply add visual polish, pass a `borderColor`. A white outer ring works well on light backgrounds:

    plot(lesmi, borderColor = "#ffffff", borderSize = 0.15)

The `borderColor` argument accepts any CSS colour string (`"#rrggbb"`, `"rgb(r,g,b)"`, named colours such as `"white"`, etc.). The `borderSize` argument is a number between 0 and 1 representing the border width as a fraction of the node’s radius — `0.15` means the ring takes up 15 % of the node radius.

For a dark-themed background, try a darker value:

    plot(lesmi, borderColor = "#222222", borderSize = 0.1)

# 3 Using the legacy gexf-js viewer

The older `gexf-js`-based viewer is preserved as `plot_gexfjs()` for users who need the file-server workflow:

    plot_gexfjs(lesmi, copy.only = TRUE)

    #> GEXF graph successfully written at:
    #> /tmp/Rtmp6wPjy5/network.gexf

`plot_gexfjs()` copies the gexf-js viewer files together with the graph to a directory (`tempdir()` by default). When called interactively with the default `copy.only = FALSE`, it also starts a local HTTP server via `servr::httd()` and opens the graph in the default browser – useful for standalone HTML reports or for users who prefer the original gexf-js interface. In non-interactive sessions (such as when this vignette is built) only the files are copied. You can also embed the graph in a Shiny app or R Markdown using the `gexfjs()` function:

    gexfjs(lesmi)

# Session info

    sessionInfo()

    #> R version 4.6.1 (2026-06-24)
    #> Platform: x86_64-pc-linux-gnu
    #> Running under: Ubuntu 24.04.4 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.26.so;  LAPACK version 3.12.0
    #>
    #> locale:
    #>  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8
    #>  [4] LC_COLLATE=C           LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8
    #>  [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C
    #> [10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
    #>
    #> time zone: UTC
    #> tzcode source: system (glibc)
    #>
    #> attached base packages:
    #> [1] stats     graphics  grDevices utils     datasets  methods   base
    #>
    #> other attached packages:
    #> [1] rgexf_0.17.0
    #>
    #> loaded via a namespace (and not attached):
    #>  [1] cli_3.6.6         knitr_1.51        rlang_1.3.0       xfun_0.60
    #>  [5] otel_0.2.0        promises_1.5.0    jsonlite_2.0.0    htmltools_0.5.9
    #>  [9] httpuv_1.6.17     XML_3.99-0.23     sass_0.4.10       rmarkdown_2.31
    #> [13] evaluate_1.0.5    jquerylib_0.1.4   fastmap_1.2.0     yaml_2.3.12
    #> [17] lifecycle_1.0.5   compiler_4.6.1    igraph_2.3.3      htmlwidgets_1.6.4
    #> [21] Rcpp_1.1.2        pkgconfig_2.0.3   later_1.4.8       digest_0.6.39
    #> [25] R6_2.6.1          servr_0.33        magrittr_2.0.5    bslib_0.11.0
    #> [29] tools_4.6.1       cachem_1.1.0