Fortran-native plotting inspired by Python's matplotlib.pyplot and https://github.com/jacobwilliams/pyplot-fortran. This library is under active development, and the API is still subject to change. There are no external dependencies for building the library itself. For verifying PDF/PNG outputs, optional tooling is recommended (see Artifact Verification).
use fortplotuse fortplot  ! Provides wp => real64 for precision
real(wp), dimension(50) :: x, y
! Generate sample data
x = [(real(i-1, wp) * 0.2_wp, i=1, 50)]
y = sin(x)
call figure()
call plot(x, y)
call title("Function Plot")
call xlabel("x")
call ylabel("y")
call xlim(0.0_wp, 10.0_wp)  ! Set x-axis limits
call ylim(-1.0_wp, 1.0_wp)   ! Set y-axis limits
call savefig("plot.png")! Multiple subplots are not yet implemented.
! Calling subplot(...) currently emits a warning ("subplot: Multiple
! subplots not yet implemented") and only the first panel is
! rendered/saved. This feature is planned for a future release.
call figure(figsize=[8.0_wp, 6.0_wp])
call subplot(2, 2, 1)  ! Will show warning
call plot(x, sin(x))
call title("Sine Wave")
call savefig("single_plot.png")  ! Only first subplot will be saveduse fortplot  ! Imports wp => real64 for precision
type(figure_t) :: fig
real(wp), dimension(50) :: x, yf
integer :: i
! Generate test data
x = [(real(i-1, wp) * 0.1_wp, i=1, 50)]
yf = sin(x)
call fig%initialize()
call fig%set_title("Function Plot")
call fig%set_xlabel("x")
call fig%set_ylabel("y")
call fig%add_plot(x, yf)
call fig%savefig("plot_oo.png")call figure(figsize=[8.0_wp, 6.0_wp])
call add_3d_plot(x, y, z, label="3D curve")
call title("3D Line Plot")
call savefig("3d_plot.png")call figure(figsize=[8.0_wp, 6.0_wp])
call plot(x, sin(x), label="sin(x)", linestyle="b-")
call plot(x, cos(x), label="cos(x)", linestyle="r--")
call plot(x, sin(2*x), label="sin(2x)", linestyle="g:")
call legend()
call savefig("trig_functions.pdf")call figure(figsize=[8.0_wp, 6.0_wp])
call title("Wave Functions: \psi(\omega t) = A e^{-\lambda t} sin(\omega t)")
call xlabel("Time \tau (normalized)")
call ylabel("Amplitude \Psi (V)")
call plot(t, damped_sine, label="\alpha decay")
call plot(t, damped_cosine, label="\beta oscillation")
call legend()
call savefig("unicode_demo.png")  ! Works in PNG, PDF, and ASCII! Basic scatter plot
call figure()
call scatter(x, y, label="Data Points")
call savefig("basic_scatter.png")
! Object-oriented scatter plot
type(figure_t) :: fig
real(wp), dimension(20) :: x, y, sizes
call fig%initialize()
call fig%scatter(x, y, label='Bubble Chart')
call fig%savefig("bubble_chart.pdf")use fortplot
implicit none
real(wp), dimension(50) :: x, y, z
integer :: i
! Generate 3D curve data
x = [(real(i-1, wp) * 0.1_wp, i=1, 50)]
y = sin(x)
z = cos(x)
! 3D surface visualization
call figure(figsize=[8.0_wp, 6.0_wp])
call add_3d_plot(x, y, z, label="3D Surface Curve")
call title("3D Surface Plot")
call savefig("surface_3d.png")
! pcolormesh for 2D heatmaps is available and working
! Array dimensions: z(ny,nx) with x(nx+1), y(ny+1) is the
! standard scientific format and does NOT emit warnings.
! C-style z(nx,ny) is also accepted; it is transposed internally.
real(wp), dimension(10, 10) :: z_data  ! ny=10, nx=10
call pcolormesh(x_grid, y_grid, z_data, colormap="viridis")call figure()
call contour_filled(x_grid, y_grid, z_data, colormap="viridis", show_colorbar=.true.)
call title("Temperature Distribution")
call xlabel("X Position")
call ylabel("Y Position")
call savefig("temperature.png")! Basic streamplot with default arrows
call figure()
call streamplot(x_grid, y_grid, u_field, v_field)
call savefig("flow_field.png")
! Streamplot with custom arrow size and style
call streamplot(x_grid, y_grid, u_field, v_field, arrowsize=1.5_real64, arrowstyle='<->')
! Streamlines without arrows
call streamplot(x_grid, y_grid, u_field, v_field, arrowsize=0.0_real64)use fortplot  ! Imports wp => real64 automatically
implicit none
! Use functional API for errorbar plots (currently supported)
real(wp), dimension(20) :: x, y, yerr, y_theory
call figure(figsize=[8.0_wp, 6.0_wp])
call errorbar(x, y, yerr=yerr, marker='o', label='Experimental data')
call plot(x, y_theory, label='Theory', linestyle='-')
call legend()
call savefig("scientific_plot.png")call figure()
call plot(x, y)
call set_xscale("log")
call set_yscale("symlog", threshold=0.01_wp)
call xlim(1.0e-3_wp, 1.0e3_wp)
call ylim(-100.0_wp, 100.0_wp)
call savefig("log_plot.pdf")- Build: make buildorfpm build
- Tests: make test(full) ormake test-ci(fast subset)
- Examples: make example(Fortran),make example_python,make example_matplotlib,make example_python_dual
Use make verify-artifacts to run key examples and validate generated PDF/PNG/txt artifacts with strict checks.
Dependencies for verification tools:
- Arch Linux: sudo pacman -S poppler ghostscript
- Ubuntu/Debian: sudo apt-get install -y poppler-utils ghostscript
- macOS (Homebrew): brew install poppler ghostscript
In PRs and issues for output-affecting fixes, include the exact commands run, artifact paths, and short excerpts from tools (e.g., pdftotext) as evidence.
- All Python examples now accept an optional --outdirto override output location.
- By default, outputs are consolidated under:
- output/example/python/fortplot/<example>/for fortplot backend
- output/example/python/pyplot/<example>/for matplotlib backend
 
- Run both backends back-to-back for quick comparisons:
make example_python_dual
# or per-example
python3 example/python/basic_plots/basic_plots.py              # fortplot
python3 example/python/basic_plots/basic_plots.py --matplotlib # matplotlib! Text annotation system is FULLY IMPLEMENTED
use fortplot
type(figure_t) :: fig
real(wp), dimension(50) :: x, y
integer :: i
! Generate sample data
x = [(real(i-1, wp) * 0.1_wp, i=1, 50)]
y = sin(x)
call figure(figsize=[8.0_wp, 6.0_wp])
call add_plot(x, y, label="Scientific Data", linestyle="b-")
call set_title("Annotated Scientific Plot")
call set_xlabel("X Variable")
call set_ylabel("Y Variable")
! Add text annotations with different coordinate systems
call add_text_annotation("Maximum", 1.57_wp, 1.0_wp, coord_type=COORD_DATA)
call add_arrow_annotation("Peak→", 1.2_wp, 0.8_wp, 1.57_wp, 1.0_wp, coord_type=COORD_DATA)
call add_text_annotation("Title Area", 0.5_wp, 0.95_wp, coord_type=COORD_FIGURE)
call savefig("annotated_plot.png")use fortplot_animation
type(figure_t) :: fig
type(animation_t) :: anim
integer :: status
real(wp), dimension(100) :: x_data, y_data
! Setup figure and initial plot
call figure(figsize=[8.0_wp, 6.0_wp])
call add_plot(x_data, y_data, label='animated data')
call title('Animation Demo')
! Create animation with update function
anim = FuncAnimation(update_frame, frames=100, interval=50, fig=fig)
call save_animation(anim, "animation.mp4", 24, status)
if (status /= 0) then
    print *, "ERROR: Animation save failed. Check ffmpeg installation."
    print *, "Windows: choco install ffmpeg"
    print *, "Linux: sudo apt install ffmpeg"
end if
contains
    subroutine update_frame(frame)
        integer, intent(in) :: frame
        ! Update plot data based on frame number
        call set_ydata(sin(x_data + real(frame, wp) * 0.1_wp))
    end subroutine update_frameWindows Support (Issue #189 Fixed): Binary pipe handling and path escaping now work correctly on Windows.
Cross-Platform FFmpeg Setup:
- Windows: choco install ffmpegor download from ffmpeg.org
- Linux: sudo apt install ffmpeg(Ubuntu/Debian) or equivalent
- macOS: brew install ffmpeg
File Validation: Use ffprobe -v error -show_format filename.mp4 to verify video integrity.
For more examples, see the example directory and run
make exampleto build and run them.
Required:
- Modern Fortran compiler (gfortran-11 or newer)
- FPM (Fortran Package Manager) or CMake
Optional:
- ffmpeg- Required for saving animations in compressed video formats (MP4, AVI, MKV)- Windows Support: Issue #189 fixed - binary pipes and path escaping work correctly
- Cross-platform: Install via choco install ffmpeg(Windows),brew install ffmpeg(macOS), or package manager (Linux)
- Setup Guide: See Windows FFmpeg Setup for Windows-specific installation
- Validation: FFprobe integration for format verification
 
Add to your fpm.toml:
[[dependencies]]
fortplot = { git = "https://github.com/lazy-fortran/fortplot" }Add to your CMakeLists.txt:
include(FetchContent)
FetchContent_Declare(
    fortplot
    GIT_REPOSITORY https://github.com/lazy-fortran/fortplot
    GIT_TAG        main
)
FetchContent_MakeAvailable(fortplot)
target_link_libraries(your_target fortplot::fortplot)Executable Stack Protection: fortplot prevents creation of executable stack segments which could be exploited for code injection attacks.
Trampoline Detection: The build system automatically detects and prevents nested functions that generate trampolines. All library code is trampoline-free for security compliance.
# Build with trampoline detection enabled
# Requires Ninja generator (install package: ninja or ninja-build)
cmake -S . -B build -G Ninja && cmake --build build -j
# Library builds successfully = trampoline-free core code
# Verify no executable stack segments
readelf -W -l build/libfortplot.a | grep -i stack
# Should return empty (no executable stack)
# Test trampoline detection (should fail on example with nested function)
fpm build --flag "-Werror=trampolines" 2>/dev/null || echo "Trampoline detection working"
# Error confirms security validation is active# FPM: Manual security flags (as documented in fpm.toml)
fpm build --flag "-Wtrampolines -Werror=trampolines"
# CMake: Automatic security flags (see CMakeLists.txt lines 36-47)
cmake -S . -B build -G Ninja && cmake --build build -j
# Standard development (FPM default)
make build  # Uses fpm.toml configurationInstall the Python package with pip:
pip install git+https://github.com/lazy-fortran/fortplot.git-  Line plots (plot) with customizable line styles and markers
-  Error bars (errorbar) with symmetric/asymmetric X/Y errors and customization
-  3D line plots (add_3d_plot) with automatic projection
-  3D surface plots (add_surface) with automatic dimension validation
-  Contour plots (contour,contourf) with custom levels and colormaps
-  Pseudocolor mesh (pcolormesh) with color limits and edge colors
-  Streamplots (streamplot) for vector field visualization with arrows
-  Enhanced scatter plots (scatter) with size/color mapping and multiple marker shapes
-  Bar charts (bar,barh)
-  Histograms (hist,histogram)
-  Images (imshow)
- PNG (raster graphics)
- PDF (vector graphics)
- ASCII (terminal display)
-  Interactive display via system viewer (show())
-  Line styles: solid (-), dashed (--), dotted (:), dashdot (-.)
-  Markers: circle (o), cross (x), square (s), diamond (D), plus (+), star (*), triangle, pentagon, hexagon
-  Format strings ('r-o','b--','g:') for matplotlib compatibility
- Colormaps: viridis, plasma, inferno, crest, coolwarm, jet, rocket, mako, flare
- Colorbars for contour and pcolormesh plots
- Legends with automatic positioning
- Scales: linear, log, symlog (with configurable threshold)
-  Axis limits (xlim,ylim)
-  Subplot grids (subplot) - Not yet implemented (shows warning)
-  Interactive display with show()(GUI detection for X11, Wayland, macOS, Windows)
-  Animation support with FuncAnimation(requiresffmpegfor video formats)- 5-Layer Validation: Comprehensive framework with size, header, semantic, and external tool checks
- False Positive Prevention: Multi-criteria validation framework
 
-  Unicode and LaTeX-style Greek letters (\alpha,\beta,\gamma, etc.) in all backends
- Security features: Executable stack protection, trampoline detection, path validation
-  Text annotations (add_text_annotation,add_arrow_annotation) with multi-coordinate systems and typography
Fortplot uses a modular architecture with focused modules under 1,000 lines each. The facade pattern maintains 100% backward compatibility:
! Your existing code works unchanged
use fortplot_pdf, only: pdf_context, create_pdf_canvas
type(pdf_context) :: ctx
call create_pdf_canvas(ctx, "plot.pdf", 800, 600)See Module Architecture Guide for developer guidelines and refactoring details.
make test# Verify heuristic procedure count budgets
make verify-complexity
# Optional: override budgets
MAX_TOTAL_PROCS=1800 MAX_PROCS_PER_FILE=50 make verify-complexityRequires ripgrep (rg) to be installed and available on PATH.
# Suppress warnings for clean test output
FORTPLOT_SUPPRESS_WARNINGS=1 make test
# Force warnings even in CI environments
FORTPLOT_FORCE_WARNINGS=1 make testSee Testing Guide for complete testing documentation.
Generate HTML documentation using FORD:
make docThe documentation is generated in build/doc/index.html and includes:
- API reference with full procedure documentation
- Source code cross-references
- Example gallery with working code
Browse documentation: Open file:///path/to/fortplot/build/doc/index.html
- Naming: use fix/<topic>,feat/<topic>,docs/<topic>,refactor/<topic>.
- Scope: keep branches focused; one unit of change per branch.
- Lifecycle: delete branches after merge; avoid long-lived topic branches.
- Hygiene: regularly prune remote-tracking branches and local branches fully merged into main.
Pruning helper:
# Dry-run: see what would be pruned
make git-prune
# Apply pruning (removes merged local branches older than 30 days)
make git-prune FORCE=1This keeps the repository navigable and avoids metadata bloat.
Mostly for the lulz and to help make Fortran great again. In addition,
there is a need for high-quality high-performance plotting directly from Fortran
with the side-effect of a higher-performance limited-feature version of matplotlib.pyplot.
Timing comparison
time make example_matplotlib
5.13s user 0.52s system 91% cpu 6.159 total
time make example_python
1.35s user 0.17s system 97% cpu 1.562 total