The following time-frequency correspondence illustration is in the wikipedia entry of the Fourier transform.


Also a gif animation version (if the animation doesn't show, please open the following image in a new window):


This picture has so much explanatory power, and I would like to replicate it in TikZ for future use.

Here is what I came up with:


\draw[->,thick,blue!90] (0,6.5,0) -- (6.2,6.5,0) node[right] {Frequency};
\draw[->,thick,red!90] (0,0,0) -- (0,6.5,0) node[below] {Time};
\draw[->,thick] (0,0,0) -- (0,0,2) node[above] {Magnitude};
\foreach \x in {0.5,1.5,2.5,3.5,4.5,5.5}{
  \draw[blue!50] (\x,0,0)
  \foreach \y in {0,0.02,...,6.28}{ 
   -- ({\x},{\y},{sin(\x*\y*(157))/sqrt(2*\x)})
  \draw[blue!90, thick] (\x,6.5,0) -- (\x,6.5,1/\x);


This is the output so far.


I have two questions:

  • How to produce that red superposed sine wave of all the blue sine waves? I don't know if there is a sum function or I have to use loop yet again?

  • How to make the camera projection in TikZ more similar to the perspective in that wikipedia illustration?

Any suggestion and tweaking of the parameters I used in the sample drawing are welcome as well.

Thanks in advance!

Update 1: Here is a new version using tikz, more readable than the first one. Yet the superposition of the sine waves are done manually...I still don't know how to use foreach to produce a sum.

\draw[->,thick,black!70] (0,6.5,0) -- (6.2,6.5,0) node[right] {Frequency};
\draw[->,thick,black!70] (0,0,0) -- (0,6.5,0) node[below right] {Time};
\draw[->,thick] (0,0,0) -- (0,0,2) node[above] {Magnitude};
\foreach \y in {0.5,1.5,...,5.5}{
\draw [cyan!50, domain=0:2*pi,samples=200,smooth] 
 plot (\y,\x, {sin(4*\y*\x r)/\y });
\draw[blue, ultra thick] (\y,6.5,0) -- (\y,6.5,1/\y);
\draw [red, thick, domain=0:2*pi,samples=200,smooth] 
plot (0,\x, {sin(4*0.5*\x r)/0.5 + sin(4*1.5*\x r)/1.5 + sin(4*2.5*\x r)/2.5 + sin(4*3.5*\x r)/3.5 + sin(4*4.5*\x r)/4.5 + sin(4*5.5*\x r)/5.5} );

The result is as follows:fourier2

4 Answers 4


Here's a way of plotting this using PGFPlots. You can collect the expression for the red curve while you're looping over the individual components using an \xdef.

Unfortunately, PGFPlots can't use a perspective projection (and even in plain TikZ I think you'll have to jump through a lot of hoops to simulate it).


    set layers=standard,
    samples y=1,
    hide axis,
    unit vector ratio*=1 2 1,
    xtick=\empty, ytick=\empty, ztick=\empty,
    \draw [on layer=background, gray!20] (axis cs:0,#1,0) -- (axis cs:10,#1,0);
    \addplot3 [on layer=main, blue!30, smooth, samples=101] (x,#1,{sin(#1*x*(157))/(#1*2)});

    \addplot3 [on layer=axis foreground, very thick, blue,ycomb, samples=2] (10.5,#1,{1/(#1*2)});
    \xdef\sumcurve{\sumcurve + sin(#1*x*(157))/(#1*2)}
\addplot3 [red, samples=200] (x,0,{\sumcurve});

\draw [on layer=axis foreground]  (axis cs:0,0,0) -- (axis cs:10,0,0);
\draw (axis cs:10.5,0.25,0) -- (axis cs:10.5,5.5,0);
  • 2
    Sweet! I wonder if we can use view={}{} for animation :)
    – percusse
    Aug 9, 2013 at 5:32

An (animatable) ePiX version is below. (I wasn't able to view the original animation, but have extrapolated from the wave equation.)

Use, e.g.,

flix --frames 120 -o fourier.gif fourier.flx

to compile.

Fourier spectrum

/* -*-flix-*- */
#include "epix.h"
using namespace ePiX;

// n treated throughout as an integer
double freq(double n) { return 2*n - 1; }
double ampl(double n) { return 1.0/freq(n); }

const unsigned int N(6); // number of harmonics
const unsigned int num_pts(120);

double MAX(2*M_PI), // max spatial coordinate
  dX(1), dY(0.5); // offsets for spectrum/frequency screens

P sw1(-MAX, 0, -2), // "waveform screen" corners
  ne1( MAX, 0,  2),
  sw2(MAX + dX,           dY, -2), // "spectrum screen" corners
  ne2(MAX + dX, freq(N) + dY,  2);

// standing sine waves of specified frequency, amplitude
P waves(double x, double n)
  return P(x, freq(n), ampl(n)*Sin(freq(n)*x)*Cos(freq(n)*full_turn()*tix()));

// sum of waves, in (x, y)-plane
P waveform(double x)
  double val(0);
  for (int i=1; i <= N; ++i)
    val += waves(x, i).x3();

  return P(x, 0, val);

domain R(P(-MAX, 1), P(MAX, N), mesh(num_pts, N - 1));

int main(int argc, char* argv[])
  if (argc == 3)
      char* arg;
      double temp1(strtod(argv[1], &arg)), temp2(strtod(argv[2], &arg));

  picture(P(-6,-3), P(12, 3), "6 x 2in");

  camera.at(P(12, -8, 4)).look_at(P(0, 0.5*N, 0)).range(25);

  // frequancy components
  plot(waves, R.slices2());

  // "screens"
  rect(sw2, ne2); // spectrum
  rect(sw1, ne1); // waveform

  // frequency components
  plot(waves, R.slices2());
  for (int i=1; i <= N; ++i)
    line(P(-MAX, freq(i), 0), P(MAX, freq(i), 0));

  // spectrum
  line(P(MAX + dX, dY, 0), P(MAX + dX, freq(N) + dY, 0));
  for (int i=1; i <= N; ++i)
      P loc(MAX + dX, freq(i), 0);
      line(loc, loc + ampl(i)*E_3);

  // waveform
  plot(waveform, -MAX, MAX, 2*num_pts);

  • Hi, Andrew, is the perspective projection automatic in ePiX? It is pretty amazing.
    – Shuhao Cao
    Aug 15, 2013 at 1:32
  • @Shuhao Cao: In a word, "yes", the ePiX camera does finite-distance point projection by default. :)
    – user86418
    Aug 15, 2013 at 11:17
  • 1
    Can you provide more details about how to install ePix and use above mentioned .flix file? What are pre-requisites of the file compilation? Apr 25, 2020 at 7:05

Here is something I have just made using this post to show constructive interferences in the time domain.

enter image description here



        \draw[->] (0,-pi,0) --++ (6,0,0) node[above right] {Frequency};
        \draw[->] (0,-pi,0) --++ (0,6.5,0) node[right] {Time};
        \draw[->] (0,-pi,0) --++ (0,0,1.5) node[above] {Magnitude};

        \draw [dashed] (1,0,0.2) --++ (4,0,0);          
        \foreach \y in {1,2,...,5}{
            \draw[blue] plot[domain = -pi:+pi, samples = 300] 
            (\y,\x,{0.2*cos(10*\y/2*(\x) r)});
            \draw[blue] (\y,-pi-0.15,0) node [left]{$f_{\y}$};
            \draw[red] (\y,0,{0.2*cos(10*\y/2*(0) r)}) node {\textbf{.}};

        \draw[red, thick] plot[domain = -pi:+pi, samples = 2000] 
        (0,\x,{0.02*sin(50*(\x) r)/(\x))});


I based the following FFT on Jake's version but tried to make it more visually appealing: I have added noise to the signals by using a random variable. The signal in the frequency is only a sinus square function that I have modulated rather empirically with an exponential function to fit my needs. There exist definitely more elegant solutions for this.

% Schematic Fourier Analysis
% Author: Tobit Flatscher
% Disclaimer
% The following code is based on a previous version of tex-stackexchange community 
% member Jake but I tried to make it more visually appealing. Some noise is added 
% to the curves in order to make them look more realistic.
% Description
% This code creates a schematic representation of an FFT analysis. A signal 
% over time is decomposed into its harmonic oscillations with a corresponding 
% frequency using a Fourier series. The amplitude of the frequency components 
% is plotted.


\usepgfplotslibrary{fillbetween} %for fill under curve


    %define colors
    \definecolor{lightgray}{rgb}{0.8, 0.8, 0.8} %grid of coordinate system, axes
    \definecolor{midgray}{rgb}{0.6, 0.6, 0.6} %layer of border
    \definecolor{darkgray}{rgb}{0.4, 0.4, 0.4} %curves, fill under curve

    \begin{tikzpicture} %create tikz picture
    \begin{axis}[ %create 3d plot within tikz
        set layers=standard, %use predefined layers
        view={50}{30}, %perspective adjustment
        domain=0:10, %plot limit in time direction
        samples y=1, %samples for frequency direction
        unit vector ratio*=1 2 1, %rescale unit vectors
        hide axis, %do not plot axes
        xtick=\empty, ytick=\empty, ztick=\empty, %no ticks on coordinate axes
        clip=false %let me plot outside the coordinate system
        %limit variables
        \def\xmax{100} %limits for curves and layers
        \def\xlayer{110} %frequency layer
        \def\sumcurve{0} %sum curve of time signal

        %frequency curves
        \pgfplotsinvokeforeach{1,2,3}{ %for each frequency component
            \draw [on layer=background, lightgray] (axis cs:0,#1,0) -- (axis cs:10,#1,0); %axes
            \addplot3 [on layer=main, darkgray, smooth, samples=200] (x,#1,{1.3*sin(2*#1*x*(157))/(#1*2)}); %plot curves (curves are somewhat arbitrary)

            \xdef\sumcurve{\sumcurve + sin(#1*x*(157))/(#1*2)} %add current curve to sumcurve

        %transparent layers
        \fill[white,opacity=0.7] (\xmin,0,\zmin) -- (\xmin,0,\zmax) -- (\xmax,0,\zmax) -- (\xmax,0,\zmin) -- cycle; %transparent layer in time space
        \fill[white,opacity=0.7] (\xlayer,\ymin,\zmin) -- (\xlayer,\ymin,\zmax) -- (\xlayer,\ymax,\zmax) -- (\xlayer,\ymax,\zmin) -- cycle; % transparent layer for frequency space

        %grid lines
        \pgfplotsinvokeforeach{\xmin,\xmin+5,...,\xmax}{ %create horizontal grid lines (time layer)
            \draw[lightgray,opacity=0.6] (#1,0,\zmin) -- (#1,0,\zmax);
        \pgfplotsinvokeforeach{\ymin,\ymin+2.5,...,\ymax}{ %create horizontal grid lines (frequency layer)
            \draw[lightgray,opacity=0.6] (\xlayer,#1,\zmin) -- (\xlayer,#1,\zmax);
        \pgfplotsinvokeforeach{\zmin,\zmin+5,...,\zmax}{ %create vertical grid lines (both layers)
            \draw[lightgray,opacity=0.6] (\xmin,0,#1) -- (\xmax,0,#1);
            \draw[lightgray,opacity=0.6] (\xlayer,\ymin,#1) -- (\xlayer,\ymax,#1);

        %borders layer
        \draw[midgray] (\xmin,0,\zmin) -- (\xmin,0,\zmax) -- (\xmax,0,\zmax) -- (\xmax,0,\zmin) -- cycle; %time space layer border line
        \draw[midgray] (\xlayer,\ymin,\zmin) -- (\xlayer,\ymin,\zmax) -- (\xlayer,\ymax,\zmax) -- (\xlayer,\ymax,\zmin) -- cycle; %frequency space layer border line

        %sum curve
        \addplot3 [samples=200] (x,0,{\sumcurve+rand/7+0.2*sin(x*400)}); %sum curve time space with added random noise

        % frequency curve
        \addplot3 [name path=f,samples=200,domain=0.5:3.5] (11,x,{rand/30+2*sin((x-0.7)*180)^200*e^(-x/2)-0.3}); %experimentally modified curve with noise

        %create fill under curve
        \addplot3 [name path=ax,draw=none,samples=2,domain=0.5:3.5] (11,x,-0.5); %create fill axis
        \addplot3 [darkgray] fill between [of=f and ax]; %create fill between curve and axis

        %create coordinate axis
        \draw[-{stealth}] (\xmin,0,\zmin-7) -- (\xmax/5.3,0,\zmin-7); %time axis
        \draw[-{stealth}] (\xlayer,\ymin,\zmin-7) -- (\xlayer,\ymin+\ymax/4,\zmin-7); %frequency axis

        %axis labels
        \node[scale=0.7] at (0,0,-21) {Time}; %axis time space
        \node[scale=0.7] at (100,22,-32) {Frequency}; %axis frequency space



2-bt FFT with TikZ

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.