Learn R Programming

ggsketch

Grammar-native, hand-drawn geoms for ggplot2 - the rough.js sketch aesthetic in pure R, with no JavaScript and no browser.

ggsketch gives you ggplot2 geoms (geom_sketch_col(), geom_sketch_line(), geom_sketch_point(), …) that render with a wobbly, hand-drawn look: roughened double-stroke outlines and hachure / cross-hatch / zigzag / dots / dashed fills. It even shades by tonal engraving — continuous tone built from hatch-line density, the way an etcher or banknote engraver works (geom_sketch_engrave()), something the fill-pattern packages can't do because they tile a motif rather than compute tone. Because the geoms are real grid grobs wrapped in ggproto, they compose with aes(), stats, scales, facets, and coords, and draw correctly on every R graphics device — screen, PNG, PDF, and SVG.

Why another sketch package?

ggsketchggrough
ApproachNative ggplot2 geoms (grid grobs)Post-hoc convert a finished plot to SVG, redraw in HTML Canvas
OutputAny device: screen / PNG / PDF / SVGHTML widget only (breaks static PDF/PNG)
Composes with aes() / stats / scales / facetsYesNo (operates on the rendered plot)
JavaScript / browserNoneRequires rough.js in a browser
StatusActiveMaintainer marks it dormant ("doesn't work with recent releases of ggplot2")

The ggrough maintainer himself notes that "a nice way to create sketchy visualisations would be a neat addition to the {ggplot2} ecosystem." ggsketch fills that gap with native geoms.

Installation

# install.packages("pak")
pak::pak("orijitghosh/ggsketch")

ggsketch is pure R (NeedsCompilation: no); its only hard dependencies are ggplot2, grid, rlang, scales, cli, and withr.

Full documentation and a gallery of every geom: https://orijitghosh.github.io/ggsketch/.

Quick start

library(ggplot2)
library(ggsketch)

df <- data.frame(product = c("Alpha","Bravo","Charlie","Delta"),
                 units   = c(34, 51, 22, 47))

ggplot(df, aes(product, units)) +
  geom_sketch_col(fill = "#7BAFD4", seed = 1L) +
  labs(title = "Units sold") +
  theme_sketch()

Every randomized routine is seeded, so a given seed always produces the same wobble — your plots are reproducible. Set a session-wide default with options(ggsketch.seed = 1L).

Showcase

Discrete fills map straight through aes(fill = …), and any geom accepts a stat - roughened columns coloured by a Brewer palette, and an Old Faithful histogram:

Points, a marginal rug, and a hand-drawn linear fit compose like any other layers; geom_sketch_bracket() adds significance brackets for pairwise comparisons:

Every fill style works on every filled geom - here fill_style = "hachure" boxplots across vehicle classes:

The 2.0 line adds whole chart families that compose with + — here a hierarchy as a hand-drawn sunburst, and weighted flows as a chord diagram:

It also simulates the medium, not just the line. The "watercolor" fill paints translucent washes that bleed wet-on-wet where regions overlap — mingling into new colours — and feather along the paper tooth set by theme_sketch(paper = ):

The geoms

FamilyGeoms
Lines & pointsgeom_sketch_line(), geom_sketch_path(), geom_sketch_point()
Bars & tilesgeom_sketch_col(), geom_sketch_bar(), geom_sketch_rect(), geom_sketch_tile(), geom_sketch_lollipop()
Areas & curvesgeom_sketch_polygon(), geom_sketch_ribbon(), geom_sketch_area(), geom_sketch_density(), geom_sketch_smooth()
Distributionsgeom_sketch_histogram(), geom_sketch_violin(), geom_sketch_boxplot(), geom_sketch_beeswarm(), geom_sketch_ridgeline(), geom_sketch_dotplot(), geom_sketch_ecdf()
Comparisons & changegeom_sketch_dumbbell(), geom_sketch_slope(), geom_sketch_bump(), geom_sketch_waterfall(), geom_sketch_funnel(), geom_sketch_pyramid()
Circular & composedgeom_sketch_circle(), geom_sketch_ellipse(), geom_sketch_segment(), geom_sketch_step()
Engraving & tonegeom_sketch_engrave() (shade an x/y/z surface by hatch-line density), geom_sketch_shade() (aes(tone = …) value → density), scale_engrave()
Charts & diagramsgeom_sketch_pie(), geom_sketch_waffle(), geom_sketch_treemap(), geom_sketch_sunburst(), geom_sketch_radar(), geom_sketch_chord(), geom_sketch_arc_diagram(), geom_sketch_alluvial(), geom_sketch_parallel(), geom_sketch_mosaic(), geom_sketch_marimekko(), geom_sketch_rose(), geom_sketch_calendar(), geom_sketch_gantt(), geom_sketch_dendrogram()
Networks & mapsgeom_sketch_edge() / sketch_graph() (force-directed layout, no graph dep), geom_sketch_sf() (hand-drawn simple-features maps)
Motionanimate_sketch() (boiling-line shimmer or draw-on reveal → GIF)
Helpers & annotationannotate_sketch(), geom_sketch_bracket() (significance brackets), annotate_sketch_arrow() / annotate_sketch_callout() (content-aware pointers), annotate_sketch_highlight() / annotate_sketch_underline() (marker emphasis), geom_sketch_text_repel() (non-overlapping labels), theme_sketch(), sketch_style() (one-call style presets), ggsketch_save()
Frame & scaleselement_sketch_line(), element_sketch_rect() (via theme_sketch(rough_frame = TRUE)), coord_sketch() / coord_sketch_polar(), scale_colour_sketch(), scale_fill_sketch(), scale_medium_discrete() (mappable drawing media), register_sketch_font()

Shared sketch parameters

ParameterMeaning
roughnessHow far points are jittered (0 = ruler-straight, ~1 default, >3 loose)
bowingHow much segments bow outward
n_passesOverlaid strokes (2 = the classic "double stroke")
seedInteger for reproducible wobble
fill_style"hachure", "cross_hatch", "zigzag", "zigzag_line", "scribble", "dots", "dashed", "stipple", "pencil_shade", "solid", "watercolor"
hachure_angle, hachure_gap, fill_weightFill line angle, spacing, and weight

How it works

Three layers, kept strictly separate:

  1. Layer 1 — pure geometry (R/core-*.R): numbers → numbers. Seeded roughening, ellipse/Bézier sampling, and an Active-Edge-Table scan-line hachure filler that handles concave polygons. No grid, no ggplot2.
  2. Layer 2 — grid grobs (R/grob-*.R): makeContent() converts to device inches and re-roughens at the real render size, so resizing re-draws cleanly.
  3. Layer 3 — ggproto geoms (R/geom-*.R): standard ggplot2 extension API.

Roughening always happens in inch space, so the look is consistent across aspect ratios and devices.

Credits & non-affiliation

The algorithms are reimplemented in original R from the published descriptions of the rough.js algorithms (Preet Shihn, 2020) and the hachure approach of Wood et al. No rough.js source is vendored, copied, or translated, and no JavaScript ships in this package. See inst/NOTICE.

ggsketch is an independent R package reimplementing the hand-drawn sketch aesthetic from first principles. It is not affiliated with, derived from, or endorsed by the rough.js project, ggrough, or any related JavaScript libraries.

rough.js is © Preet Shihn and licensed MIT. ggsketch is licensed MIT.

Copy Link

Version

Install

install.packages('ggsketch')

Version

2.0.0

License

MIT + file LICENSE

Issues

Pull Requests

Stars

Forks

Maintainer

Arijit Ghosh

Last Published

July 17th, 2026

Functions in ggsketch (2.0.0)

geom_sketch_alluvial

Sketchy alluvial / Sankey diagram
engrave_fill

Fill a region with tonal engraving (line density follows a tone field)
engrave_ladder

Build a tonal hatch ladder for engraving fills
geom_sketch_arc_diagram

Sketchy arc diagram
element_sketch_paper

Paper-ground theme element
draw_key_sketch

Sketchy legend keys
force_layout

Force-directed graph layout (Fruchterman-Reingold)
curve_fill

Flatten a closed Bezier boundary to a polygon, then fill
GeomSketchAbline

Sketchy reference lines
element_sketch_line

Hand-drawn theme elements
GeomSketchChicklet

Sketchy chicklet chart
GeomSketchBracket

Sketchy significance / comparison brackets
GeomSketchCallout

Sketchy callouts (boxed labels with a leader arrow)
geom_sketch_chord

Sketchy chord diagram
StatSketchCalendar

Sketchy calendar heatmap
GeomSketchBoxplot

Sketchy boxplot
StatSketchBeeswarm

Sketchy beeswarm chart
GeomSketchArrow

Sketchy content-aware arrows
geom_sketch_bump

Sketchy bump (ranking) chart
geom_sketch_bin2d

Sketchy 2-D bin heatmap
geom_sketch_dendrogram

Sketchy dendrogram (hierarchical-clustering tree)
GeomSketchContourFilled

Sketchy filled contour and 2-D density bands
geom_sketch_density2d

Sketchy 2-D density contours
GeomSketchCol

Sketchy column / bar geom
GeomSketchEllipse

Sketchy circle and ellipse geoms
geom_sketch_contour

Sketchy contour lines
GeomSketchDotplot

Sketchy dot plot
geom_sketch_density

Sketchy density geom
geom_sketch_count

Sketchy count geom
GeomSketchCurve

Sketchy curved connector
geom_sketch_ecdf

Sketchy empirical cumulative distribution
GeomSketchEdge

Sketchy network edges and nodes
geom_sketch_jitter

Sketchy jittered points
geom_sketch_function

Sketchy function curve
GeomSketchGantt

Sketchy Gantt / timeline chart
GeomSketchDumbbell

Sketchy dumbbell chart
geom_sketch_histogram

Sketchy histogram and frequency polygon
GeomSketchEngrave

Sketchy engraving: tonal shading by hatch-line density
StatSketchFunnel

Sketchy funnel chart
GeomSketchHex

Sketchy hexagonal heatmap
GeomSketchMarkCircle

Sketchy bounding marks around point groups
GeomSketchLollipop

Sketchy lollipop chart
geom_sketch_marimekko

Sketchy Marimekko (variable-width stacked bar) chart
GeomSketchLine

Sketchy line geom
StatSketchPie

Sketchy pie and donut charts
geom_sketch_mosaic

Sketchy mosaic plot
GeomSketchLinerange

Sketchy interval geoms
GeomSketchMarkHull

Sketchy hull marks around point groups
geom_sketch_parallel

Sketchy parallel-coordinates plot
GeomSketchPath

Sketchy path geom
geom_sketch_quantile

Sketchy quantile regression lines
StatSketchPyramid

Sketchy population pyramid
StatSketchRadar

Sketchy radar (spider) chart
geom_sketch_qq

Sketchy quantile-quantile plot
GeomSketchRect

Sketchy rectangle / tile geom
GeomSketchRibbon

Sketchy ribbon and area geoms
geom_sketch_rose

Sketchy coxcomb / Nightingale rose chart
GeomSketchPoint

Sketchy point geom
StatSketchDensityRidges

Sketchy ridgeline (joyplot)
GeomSketchPolygon

Sketchy polygon geom
GeomSketchSpoke

Sketchy spoke geom
GeomSketchSegment

Sketchy segment and step geoms
geom_sketch_sunburst

Sketchy sunburst (hierarchy) chart
GeomSketchSmooth

Sketchy smoothed conditional mean
StatSketchStream

Sketchy streamgraph
GeomSketchRug

Sketchy marginal rug
GeomSketchSfPolygon

Sketchy simple-features (sf) maps
GeomSketchTextRepel

Sketchy repelled text and labels
GeomSketchSlope

Sketchy slope graph
geom_sketch_text

Sketchy text and labels
ggsketch_check_fonts

Check for optional handwriting fonts
StatSketchTreemap

Sketchy treemap
StatSketchWaterfall

Sketchy waterfall chart
StatSketchWaffle

Sketchy waffle chart
ggsketch-package

ggsketch: Grammar-Native Hand-Drawn Geoms for 'ggplot2'
hachure_fill

Fill a polygon with hachure lines using the AET scan-line algorithm
leader_path

Route a leader line from a box edge to a target
hachure_fill_multi

Fill a multi-ring region with hachure lines (hole-aware)
ggsketch_save

Save a sketch plot with a font-aware device
GeomSketchViolin

Sketchy violin plot
paper_grain

Wash-feathering grain factor for a paper ground
paper_primitives

Build the vector primitives for a paper ground
roughen_polyline

Roughen a polyline (multiple connected segments)
rough_arc

Roughen an elliptical arc into one or more sketch stroke paths
paper_spec

Palette + layout spec for a paper ground
rough_ellipse

Roughen an ellipse into one or more sketch stroke paths
scale_medium_discrete

Discrete scale for the drawing medium aesthetic
repel_layout

Repel overlapping label boxes away from each other and their anchors
rough_bezier

Roughen a cubic Bezier curve
register_sketch_font

Register a handwriting font for reproducible sketch text
sketch_band_grob

Create a sketchy filled-band grob (hole-aware region)
scale_sketch

Sketch colour and fill scales
sketch_ellipse_grob

Create a sketchy ellipse / circle grob
scale_pressure_continuous

Continuous scale for the sketch pressure aesthetic
sketch_dotplot_grob

Create a sketchy dot-plot grob (stacked circular dots)
scale_roughness_continuous

Continuous scale for the sketch roughness aesthetic
sketch_arrow_grob

Create a sketchy arrow grob
sketch_callout_grob

Create a sketchy callout grob (boxed label + leader arrow)
scale_tone_continuous

Continuous scale for the engraving tone aesthetic
sketch_arrowheads

The available arrowhead styles
sketch_repel_grob

Create a sketchy repelled-label grob
sketch_papers

The available paper grounds
sketch_path_grob

Create a sketchy path grob
sketch_engrave_grob

Create a sketchy engraving grob (tonal cross-hatch by line density)
sketch_graph

Build node and edge data for a sketch network
sketch_fill

Dispatch fill-style to the appropriate Layer-1 fill function
sketch_polygon_grob

Create a sketchy polygon grob with optional hachure fill
sketch_media

The available drawing media
sketch_fill_multi

Dispatch fill-style for a multi-ring (hole-aware) region
sketch_palette

The ggsketch qualitative colour palette
sketch_style

Apply a complete hand-drawn style in one call
sketch_wedge_grob

Create a sketchy pie/donut-wedge grob
spray_scatter

Scatter a cloud of dots along a path (airbrush / spray)
stroke_profile

Width-profile presets for stroke_ribbon()
stroke_ribbon

Build a variable-width stroke as a closed polygon ribbon
treemap_layout

Squarified treemap layout
theme_sketch

A hand-drawn theme for ggplot2
sketch_styles

The available style presets
sketch_spray_grob

Create an airbrush / spray stroke grob
watercolor_wash

Build a watercolour wash for a polygon region
wash_bleed

Pigment bleed between two overlapping wash regions
watercolor_wash_multi

Build a hole-aware watercolour wash for a multi-ring region
sketch_stroke_grob

Create a variable-width sketch stroke grob
boil_gganimate

Boil a gganimate animation (sketch wobble + data transitions)
CoordSketchPolar

A hand-drawn polar coordinate system
CoordSketch

A hand-drawn coordinate system
animate_sketch

Animate a sketch plot by "boiling" its lines
annotate_sketch_callout

Add a one-off sketchy callout annotation
annotate_sketch_arrow

Add a one-off sketchy arrow annotation
arrowhead

Build the ideal paths for one arrowhead
GeomSketchNull

Null sketch geom (infrastructure smoke-test only)
annotate_sketch

Sketchy annotations
annotate_sketch_highlight

Highlighter swipes and hand-drawn underlines