Giter Club home page Giter Club logo

pyfda's Introduction

pyfda

Python Filter Design Analysis Tool

PyPI version Downloads/mo.

MIT licensed ReadTheDocs build_pyinstaller build_flatpak

pyfda is a tool written in Python / Qt for analyzing and designing discrete time filters with a user-friendly GUI. Fixpoint filter implementations (for some filter types) can be simulated and tested for overflow and quantization behaviour in the time and frequency domain.

Screenshot

Screenshot pyfda, specifications and 3d surface plot Screenshot pyfda, specs and transient response Screenshot pyfda, coefficients and transient response
Screenshot pyfda, poles / zeros and 3D contour plot Screenshot pyfda, coefficients and pole-zero-plot Screenshot pyfda, infos and pole-zero-plot with embedded amplitude magnitude
Screenshot pyfda, poles / zeros and transient response with complex stimulus Screenshot pyfda, fixpoint and transient response Screenshot pyfda, fixpoint and transient response (frequency)

License

pyfda source code ist distributed under a permissive MIT license, binaries / bundles come with a GPLv3 license due to bundled components with stricter licenses.

Installing, running and uninstalling pyfda

For details, see INSTALLATION.md.

Binaries

Binaries can be downloaded under Releases for versioned releases and for a latest release, automatically created for each push to the main branch.

Self-extracting archives for 64 bit Windows, OS X and Ubuntu Linux are created with pyInstaller. The archives self-extract to a temporary directory that is automatically deleted when pyfda is terminated (except when it crashes), they don't modify the system except for two ASCII configuration files and a log file. No additional software / libraries need to be installed, there is no interaction with existing python installations and you can simply overwrite or delete the executables when updating. After downloading the Linux archive, you need to make it executable (chmod 775 pyfda_linux).

Binaries for Linux are created as Flatpaks as well (currently defunct) which can also be downloaded from Flathub. Many Linux distros have built-in flatpak support, for others it's easy to install with e.g. sudo apt install flatpak. For details check the Flatpak home page.

pip

Python 3.8 and above is supported, there is only one version of pyfda for all operating systems at PyPI. As pyfda is a pure Python project (no compilation required), you can install pyfda the usual way, required libraries are downloaded automatically if missing:

> pip install pyfda

Upgrade:

> pip install pyfda -U

Uninstall:

> pip uninstall pyfda

Starting pyfda

A pip installation creates a start script pyfdax in <python>/Scripts which should be in your path. So, simply start pyfda using

> pyfdax

The following libraries are required and installed automatically by pip when missing.

Optional libraries:

  • mplcursors for annotating cursors
  • docutils for rich text in documentation
  • xlwt and / or XlsxWriter for exporting filter coefficients as *.xls(x) files

conda

If you're working with Anaconda's packet manager conda, there is a recipe for pyfda on conda-forge since July 2023:

> conda install --channel=conda-forge pyfda

You should install pyfda into a new environment to avoid unwanted interaction with other installations.

git

If you want to contribute to pyfda (great idea!), fork the latest version from https://github.com/chipmuenk/pyfda.git and create a local copy using

> git clone https://github.com/<your_username>pyfda

This command creates a new folder pyfda at your current directory level and copies the complete pyfda project into it. This Github tutorial provides a good starting point for working with git repos.

pyfda can then be installed (i.e. creating local config files and the pyfdax starter script) from local files using

> pip install -e <YOUR_PATH_TO_PYFDA_setup.py>

Now you can edit the code and test it. If you're happy with it, push it to your repo and create a Pull Request so that the code can be reviewed and merged into the chipmuenk/pyfda repo.

Building pyfda

For details on how to publish pyfda to PyPI, how to create pyInstaller and Flatpak bundles, see BUILDING.md.

Customization

The location of the following two configuration files (copied to user space) can be checked via the tab Files -> About:

  • Logging verbosity can be controlled via the file pyfda_log.conf
  • Widgets and filters can be enabled / disabled via the file pyfda.conf. You can also define one or more user directories containing your own widgets and / or filters.

Layout and some default paths can be customized using the file pyfda/pyfda_rc.py, at the moment you have to edit that file at its original location.

Features

Filter design

  • Design methods: Equiripple, Firwin, Moving Average, Bessel, Butterworth, Elliptic, Chebyshev 1 and 2 (from scipy.signal and custom methods)
  • Second-Order Sections are used in the filter design when available for more robust filter design and analysis
  • Fine-tune manually the filter order and corner frequencies calculated by minimum order algorithms
  • Compare filter designs for a given set of specifications and different design methods
  • Filter coefficients and poles / zeroes can be displayed, edited and quantized in various formats
  • Fixpoint filters based on the integrated Fixed() class or on the Amaranth hardware description language.

User Interface

  • enhanced Matplotlib NavigationToolbar (nicer icons, additional functions)
  • tooltips for all UI widgets and help files
  • specify frequencies as absolute values or normalized to sampling or Nyquist frequency
  • specify ripple and attenuations in dB, as voltage or as power ratios
  • enter values as expressions like exp(-pi/4 * 1j) using numexpr syntax

Graphical Analyses

  • Magnitude response (lin / power / log) with optional display of specification bands, phase and an inset plot
  • Phase response (wrapped / unwrapped) and group delay
  • Pole / Zero plot
  • Transient response (impulse, step and various stimulus signals) in the time and frequency domain. Define your own stimuli like abs(sin(2*pi*n*f1)) using numexpr syntax and the UI.
  • 3D-Plots (H|(f)|, mesh, surface, contour) with optional pole / zero display

Import / Export

  • Filter designs in JSON, pickled and in numpy's NPZ-format
  • Coefficients and poles/zeros as comma-separated values (CSV), in CMSIS format in numpy's NPY- and NPZ-formats, in Excel (R), as a Matlab (R) workspace or in FPGA vendor specific formats like Xilinx (R) COE-format
  • Transient stimuli (y[n] tab) as wav and csv files

Target group

  • Educators and students: Provide an easy-to-use FOSS tool for demonstrating DSP stuff and interactive filter design that also works with the limited resolution of a beamer.
  • Fixpoint filter designer: Recursive fixpoint filter design has become a niche for experts. Convenient design and simulation support (round-off noise, stability under different quantization options and topologies) could attract more designers to these filters that are easier on hardware resources and much more suitable especially for uCs and low-budget FPGAs.

Release History

For details, see CHANGELOG.md.

Planned features (help wanted)

  • Dark mode
  • Use Amaranth to simulate fixpoint filters and export them in HDL format
  • Graphical modification of poles / zeros
  • Document filter designs in PDF / HTML format
  • Design, analysis and export of filters as second-order sections, display and edit them in the P/Z widget
  • Multiplier-free filter designs (CIC, GCIC, LDI, ΣΔ, ...) for fixpoint filters with a low number of multipliers (or none at all)
  • Analysis of different fixpoint filter topologies (direct form, cascaded form, parallel form, ...) concerning overflow and quantization noise

pyfda's People

Contributors

cfelton avatar chipmuenk avatar gitter-badger avatar honahursey avatar jeanibarz avatar mivade avatar murmele avatar ryanvolz avatar sp1nelson avatar sriyash25 avatar tspiteri avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyfda's Issues

Changing entries in dynamic subwidgets does not trigger "sigSpecsChanged"

For some filter designs, dynamic subwidgets with text entries, combo boxes etc. are created (e.g. grid_density for equiripple designs).

Changing these subwidgets does not trigger the signal sigSpecsChanged.

Reminder: Check whether signals are properly disconnected when reloading filters !

pyFDA doesn't shut down properly

When closing the application by closing the window or by clicking the "Quit" button, the python kernel crashes (Windows 7, 64 bit). I don't know whether the same happens under other OS's.

Enhanced display and editing in P/Z widget

Display P/Z in various formats using itemDelegates similar to filter_coeffs.py

  • Cartesian
  • Polar
  • SOS

Sort the P/Z according to:

  • Frequency
  • Magnitude
  • Real part
  • Imaginary part

More options in the editor

  • Option for mirroring P/Z (w/ and without copying) at the UC or the x-axis
  • Option for limiting P/Z to a selectable magnitude while maintaining the angle

Interactive Editing
When a dedicated signal-slot connection between the editor and the plot window has been established, it should be easy to:

  • Select P/Z in the editor and highlight it in the plot window

Providing interactive options as explained in https://stackoverflow.com/questions/29277080/efficient-matplotlib-redrawing allows to

  • Select P/Z in the plot window and highlight it in the editor
  • Add / Delete P/Z in the plot window
  • Drag & Drop P / Z in the plot window

pyQt5 is not supported

... hopefully, this is easy once the crash issue has been fixed.

pyFDA should detect at startup whether pyQt5 is available and set a corresponding global flag for doing something like

if PYQT5: from PyQt5 import QtGui from PyQt5.QtCore import pyqtSignal else: from PyQt4 import QtGui from PyQt4.QtCore import pyqtSignal

For details see http://pyqt.sourceforge.net/Docs/PyQt5/pyqt4_differences.html

  • only new-style signal-slot connections have been used so far, so this shouldn't be a problem
  • "disconnect() takes no arguments and disconnects all connections to the QObject instance" - this could be problematic
  • "PyQt4’s QtGui module has been split into PyQt5’s QtGui, QtPrintSupport and QtWidgets modules" - this needs to be checked

input_filter.py throws a type error when filter type does not exist

How to reproduce the issue:

  • run python -m pyfda.input_widgets.input_filter
    (Lowpass, FIR, Equiripple should be selected)
  • select 'Minimum Order'
  • select 'Bandpass' ... and:

379: self.fo = foList[0] # use first list entry from filterTree

TypeError: 'dict_keys' object does not support indexing

Here, a minimum order bandpass is not contained in the filter dict. Consequently, it should not be selectable at all. But it is and it produces the error above.

conf files should be under /etc, log files should be in /var/log

Currently, it installs conf files into the python package directory:

	/usr/local/lib/python2.7/site-packages/pyfda/pyfda_log.conf
	/usr/local/lib/python2.7/site-packages/pyfda/pyfda_log_debug.conf

Then the app fails:

IOError: [Errno 13] Permission denied: '/usr/local/lib/python2.7/site-packages/pyfda/pyfda.log'

because this is a wrong location for the log file.

Installing Pyfda

Wanted to install the new version. Used the described command "python setup.py -install" form the mainpage. If you look in the setup help using "python setup.py --help", the install command is "python setup.py install". Without the "-" which works perfectly fine :)

Font size, left pane width and tab labels width on high DPI displays

Hello. Thank you for this useful tool. I installed it on my laptop with Windows 10, which is set to 150% scaling, 144 dpi. Unfortunately, everything was so tiny, to the point of being unreadable.

A possible reason is that you specify font sizes in absolute pixels (12px) which probably does not allow scaling on high dpi displays. I've changed font sizes to points, by replacing in pyfda_rc.py, 12px with 10pt (three occurrences). This way, the font scales well and the interface looks good both on standard display and on the high dpi one.

The second problem was that the left pane was way too narrow, most of the list box items were replaced with ellipsis. For my own purposes, I changed the pane width (pyfdax.py, line 97) to setMaximumWidth(420) and now it looks good on my display. Maybe this value should be set automatically based on screen width. Maybe this line is not even necessary.
Edit: I've just checked this on my laptop: removing this line gives a nice looking layout.

An another thing: tabs labels were not fitting in the tabs. You specify the minimum width of tab as 10ex (pdyfa_rc.py, line 165). ex is the height unit, width should be specified with em because it makes sense. Changing this to min-width: 5em; gave the result that looks very nice on my screen.

Please consider making these changes in your code.

Numerical percision issues with tight filter constraints

I've noticed an issue in the calculation of group delay and magnitude response when designing a highly constrained filter: Lowpass, IIR (N=10), Chebychev 2, Fc = 0.01, Asb = 60 . This issue appears to be caused by the use of the transfer function polynomials in the calculation and I have been able to correct the group delay by changing the calculation to use zero-pole values instead of the polynomials (see code below). I did some testing and it looks like the magnitude response appears to be corrected if the filter is designed in the analogue domain however this affects subsequent frequency and group delay calculations and the coefficients need to be transformed to the digital domain.

'''
add to pyfda/plot_widgets/plot_tau_g.py (line 82) 
remove/comment out currently defined w and tau_g variables
'''
        zz = fb.fil[0]['zpk'][0]
        pp = fb.fil[0]['zpk'][1]

        G = np.zeros(rc.params['N_FFT'])
        w = np.arange(0,1, 1/rc.params['N_FFT']) * np.pi
        for i in range(zz.shape[0]):
            rz = np.abs(zz[i])
            az = np.angle(zz[i])
            G += (-rz*np.cos(w-az) + rz**2) / (rz**2 - 2*rz*np.cos(w-az) + 1)
        for i in range(pp.shape[0]):
            rp = np.abs(pp[i])
            ap = np.angle(pp[i])
            G -= (-rp*np.cos(w-ap) + rp**2) / (rp**2 - 2*rp*np.cos(w-ap) + 1)
        tau_g = G

Pass designed filter in SOS format to myHDL methods

File: pyfda/hdl_generation/hdl_specs.py

Since integrating the SOS (second-order sections) from scipy.signal, the designed filter can be retrieved in SOS format from the central dict via

fb.fil[0]['sos']

Using this information to simulate and design fixpoint filters using myHDL would be a major step towards an universal fixpoint filter synthesis tool.

Target frequency / amplitude specs are not consistent for manual filter order designs

For manual filter order design algorithms, target frequency and amplitude specs should be displayed although they are not used for the design process.

They are needed though for displaying the spec limits. Currently, this doesn't work.

The root cause for this lies in the module filter_specs.py where the amp. and freq. parameters for minimum order filter design are misused as target specs. This has to be fixed by adding 'targ' parameters in the filter specifications of all filter modules and using them for the spec limits.

PZ-normalization

When adding / deleting poles or zeros, the gain factor k remains unchanged. This changes the overall filter gain e.g. in the passband.
File: input_widgets/input_pz.py
Method: normalizeCoeffs()

Canvas of hidden plot tabs are not redrawn when the window size is changed

The active plot tab is redrawn when the size of the canvas is changed via an event filter in plotTabWidgets that calls the redraw routines of all plot widgets after a delay of 500 ms.

However, the hidden plot tabs are not affected by this, requiring a manual redraw (the brush icon).

This could be solved by only redrawing the active tab window upon resize and redrawing the other tabs when they become active.

Designing IIR filters with N > 30 produce errors and sometimes even crash

Certain choices caused these errors:

[  ERROR] [pyfda.pyfda_lib:1147] could not broadcast input array from shape (0) into shape (2)
[  ERROR] [pyfda.filter_factory:216] Err. Code 18:

Error calling method 'BPman' of class 'Butter':
The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
[WARNING] [pyfda.input_widgets.filter_specs:286] start_design_filt:
Attribute not found.
Unknown design method.

But it is hard to tell what's wrong from messages.

Screen looks like this:

pyfda-errs1

Complex valued filters throw an error in fil_convert

When creating a complex valued filter (e.g. by deleting a single pole), the zpk2sos algorithm throws an error message (probably also true for tf2sos).

This condition should be detected in pyfda_lib.fil_convert, and in this case fil[0]['sos'] should be set to 'None'.

RuntimeError on systems with both PyQt4 and PyQt5 installed

RuntimeError: the PyQt4.QtCore and PyQt5.QtCore modules both wrap the QObject class

This is a regression that had been fixed with PR #11 and was re-introduced by me while adding pyQt5 compatibility and hunting down another but.

As far as I understand the problem, matplotlib inherits from QObject which is ambiguous when both libraries are present on the system.

Solution: Specifiy the backend for matplotlib when it is imported for the first time:

    import matplotlib
    matplotlib.use("Qt5Agg")

Changing filter type fires inputTabWidgets.sigSpecsChanged four times

Each time the design method, response type or filter order (min/man) is changed, the specs for frequency, amplitude, filter order and target amp/frequency are updated, firing a local sigSpecsChanged signal in each subwidget.

These signals are connected to input_widgets.input_specs.sigSpecsChanged which is connected
to input_widgets.input_tab_widgets.sigSpecsChanged.

Finally, in pyfdax.py, the signal triggers pltTabWidgets.update_view, updating the subplots four times causing unneccessary delay.

ToDo: make creation of sigSpecsChanged in the subwidgets more intelligent (was the update caused by changing individual specs or by reloading all?)

Specs are only checked at the corners of pass and stop band

When creating a design that fulfills the specs at the corners of pass and stop band but violates them somewhere inbetween, this is not flagged in the Info tab (input_widgets/input_info.py).

The reason is that so far only the band corners are checked ...

Update all QLineEdits to using safe_eval() and to a selectable number of digits

Some entry fields are not yet filtered through safe_eval() and thus potentially unsafe and / or throw errors in the console. They should also use event_filter for displaying a limiting number of digits.

  • Update simple_eval() to new version
  • Update all QLineEdit fields to use safe_eval() with backup parameter
  • Add a paramter to safe_eval() to check / convert e.g. for integer type
  • Implement QEvent filters to allow editing the values with full resolution

Integer QLineEdits:

  • select_filter.py -> set_filter_order (): filter order
  • equiripple.py: grid_density
  • ma.py: M, stages
  • plot_impz.py: number of data points

Float QLineEdits:

  • plot3D: various fields
  • Windowed FIR: various fields
  • filter_coeffs.py -> set_coeffs_zero: scale (float instead of int), W, WI, WF

BUG: Initial corner frequency specs are identical sometimes

In some cases, default corner frequencies e.g. for pass- and stop band are identical. This means, something goes wrong between reading frequency specs from the global filter dict and displaying / storing them back to the filter dict. Could be related to issue #6.

Don't produce a warning but "INFO" when default base dir does not exist

The base directory that is defined in pyfda_rc throws a warning in pyfdax.py when it doesn't exist. This might confuse users, change it to level INFO.

This also gives a message during pip installation:

Detected hard-coded path in text file bin/pyfdax
Detected hard-coded path in text file bin/pyfdax_no_term

Pole/Zero Editor

The QTableWidget for viewing and editing pole/zeros is still missing.

New Elliptic Filter fixes

Shrink name of new elliptic zero phase filter in pullDown menu.
Improve name of file_io pullDown menu for custom text file output

PDF report generation

It would be very helpful to create a design report for a generated filter (PDF) containing relevant plots and tables.

A very promising approach is described in

https://towardsdatascience.com/creating-pdf-reports-with-python-pdfkit-and-jinja2-templates-64a89158fa2d

where HTML to PDF conversion is achieved with pdfkit, HTML generation is controlled with jinja2 (needs to be installed).

As an alterative, PDF could be generated directly using fpdf:
https://stackoverflow.com/questions/47195075/insert-base64-image-to-pdf-using-pyfpdf

Fc does not update filter properly

First of this tool looks great! thank you so much for your effort in putting this together.

I'm trying to use this to plot / design low and high pass filters though noticed that in changing the Fc value the filters are not changing. The filters always seem to be calculated using the default Fc value for the filter type. Is there a known work around for this issue? The filter order does seem to be working properly.

Use QClipboard for various tasks

The QClipboard widget is an elegant way for importing (e.g. coefficients, P/Z, ...) and exporting (e.g. screenshots) data - this would enhance interfaces and functionality.

See e.g. http://python.6.x6.nabble.com/Paste-entire-column-into-QTableView-from-Excel-td5016995.html

Clipboard functionality has been started as qcopy_to_clipboard() and qcopy_from_clipboard() in the library pyfda_qt.lib: The following steps are required / are working already:

  • qcopy_to_clipboard() with all cells as float
  • qcopy_to_clipboard() with selected cells in current format: second column (empty) is always copied as empty string. When only some cells are selected, number, order and content are not correct.
  • qcopy_from_clipboard() as float or in selected format
  • combine with csv export and import
  • import / export in other formats than float doesn't work yet

Enhance IIR elliptical filter

Add new IIR non-causal elliptical filter. This creates an elliptic filter with the square root of the Amplitude specs, then later the code will square the filter coefficients to create a filter with zero phase that could be applied to signal data that has been stored to a file. Applying these filter coefficients is not done in time order, so the input signal data would need to have been stored.
Update displays to show correct Frequency response, phase response, poles/zeros, group delay and impulse response for the new elliptic filter.

Improve debug interface

Debug information should be presented more structured and detailed (input_widgets/input_info.py):

  • One window for all debug info (filter tree, filter dict), selectable via combo box
  • Select debug level from UI
  • Display logging info
  • Display versions of Python interpreter and imported modules
  • Export debug info to text files

Several bugs in dark UI theme

  • Selection range of zoom tools is not visible with dark theme (black on black)
  • The stimulus in the plot_impz.py is not visible
  • Grid lines of all QTableView > QHeader widgets are dark instead of white
  • Separator lines of matplotlibs NavigationToolbar are white instead of dark
  • Icon are hardly visible with dark theme

Selecting different sets of icons depending on the theme could be achieved by:

  • using QFileSelector (qt5 only, unfortunately)
  • working with QResource by specifying different prefixes, e.g.
    <qresource prefix="/images/icons/dark">
    <file alias="quantize.svg">quantize.svg</file>
    </qresource>
    
    

This would require compiling to e.g. qrc_resources_dark.py and qrc_resources_light.py and make a conditional import.

The following style sheet could be used or modified:
https://github.com/ColinDuquesnoy/QDarkStyleSheet

Set number of displayed digits and format in P/Z

Especially for complex P/Z and coefficients, the input fields become very wide. For low-resolution displays it would be nice to reduce the number of displayed widgets.

However, internally and when editing a field, the full resolution needs to be maintained.
This could be achieved by keeping a shadow copy.

  • Display coefficients with the number of digits from pyfda_rc.params['FMT_ba'] and fixpoint formats
  • Display P/Z with selectable number of digits from pyfda_rc.params['FMT_pz'] and formats

Several bugs in fixpoint display / editing in pyfda_fix_lib.py

  • Scaling parameter is not used yet
  • Illegal values in CSD return zero
  • int_places calculation for CSD doesn't always make sense
  • Frmt2float for fractional hex yields wrong results, probably due to incorrect use of MSB
  • WI < 0 yields wrong scaling for decimal

BUG: Filter corner frequencies are not always sorted correctly

In some cases it is not possible to design a filter although the displayed filter specifications seem to be o.k. It seems that the corner frequencies are passed in the wrong order to the actual filter design routine.

This bug occurs from time to time when changing the response type (e.g. from bandpass to bandstop).

Most likely, the bug is in input_widgets/input_freq_specs.py, input_specs.py or input_targ_specs.py.

Specs for IIR bandpass and bandstop filters are not shown correctly

In contrast to FIR filter design, IIR filters only can be designed with identical amplitude specs for both pass- resp. stopbands.

The UI does not allow to enter e.b. A_PB2 or A_SB2, hence, prior settings are used for displaying the specs in H(f) and the info tab.

Solution: Copy A_PB -> A_PB2 resp. A_SB -> A_SB2 in the filter design routines.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.