Git Product home page Git Product logo

barraider / streamdeck-tools Goto Github PK

View Code? Open in Web Editor NEW
450.0 22.0 68.0 2 MB

The Stream Deck Tools library wraps all the communication with the Stream Deck app, allowing you to focus on actually writing the Plugin's logic

Home Page: https://barraider.com

License: MIT License

C# 82.08% Batchfile 0.29% HTML 0.36% JavaScript 3.27% CSS 14.00%
streamdeck-tools streamdeck streamdeck-sdk streamdecksdk elgato-stream-deck stream-deck library plugin management barraider

streamdeck-tools's Introduction

BarRaider's Stream Deck Tools

C# library that wraps all the communication with the Stream Deck App, allowing you to focus on actually writing the Plugin's logic.

Build Status โ€ƒNuGet

Author's website and contact information: https://barraider.com

Stream Deck+ Support

Instead of PluginBase, Derive from either KeypadBase (if you don't support dials), EncoderBase (for only dials), KeyAndEncoderBase (for both keys and dials)

Getting Started

Introducing our new wiki packed with usage instructions, examples and more.

Dev Discussions / Support

Discord: Discuss in #developers-chat in Bar Raiders

Downloadable Resources

  • StreamDeck-Tools Template for Visual Studio (2019/2022) - Automatically creates a project with all the files needed to compile a plugin. This is the best way to start a new plugin!
  • Install.bat - Script that quickly uninstalls and reinstalls your plugin on the streamdeck (edit the batch file for more details). Put the install.bat file in your BIN folder (same folder that has Debug/Release sub-folders)
  • EasyPI - Additional library used to easily pass information from the PI (Property Inspector) to your plugin.
  • Profiles Downloadable empty profiles for the XL (32-key), Classic (15-key), Mini (6-key) and Mobile devices at https://barraider.com/profiles

Library Features

  • Encapsulates all the communicating with the Stream Deck, getting a plugin working on the Stream Deck only requires implementing the PluginBase class.
  • Sample plugin now included in this project on Github
  • Built-in integration with NLog. Use Logger.LogMessage() for logging.
  • Auto-populate user settings which were modified by the Property Inspector
  • Access the Global Settings from anywhere in your code
  • Simplified working with filenames from the Stream Deck SDK.
  • PluginActionId attribute let's you easily associate your code to a specific action defined in the manifest.json
  • Large set of helper functions to simplify creating images and sending them to the Stream Deck.

Change Log

Version 6.2

  • Support for .NET 8.0

Version 6.1

  • Support for new DialDown and DialUp events.
  • Removed support for deprecated DialPress event

Version 6.0

  1. Merged streamdeck-client-csharp package into library to allow better logging of errors
  2. Added support for SD+ SDK
  3. Increased timeout of connection to Stream Deck due to the Stream Deck taking longer than before to reply on load
  4. Added error catching to prevent 3rd party plugin exception to impact communication

Version 3.2 is out!

  • Created new ISDConnection interface which is now implemented by SDConnection and used by PluginAction.
  • GlobalSettingsManager now has a short delay before calling GetGlobalSettings(), to reduce spamming the Stream Deck SDK.
  • Updated dependencies to latest version

Version 3.1 is out!

  • Updated Logger class to include process name and thread id

Version 3.0 is out!

  • Updated file handling in Tools.AutoPopulateSettings and Tools.FilenameFromPayload methods
  • Removed obsolete MD5 functions, use SHA512 functions instead
  • Tools.CenterText function now has optional out textFitsImage value to verify the text does not exceed the image width
  • New Tools.FormatBytes function converts bytes to human-readable value
  • New Graphics.GetFontSizeWhereTextFitsImage function helps locate the best size for a text to fit an image on 1 line
  • Updated dependency packages to latest versions
  • Bug fix where FileNameProperty attribute

Version 2.7 is out!

  • Fully wrapped all Stream Deck events (All part of the SDConneciton class). See "Subscribing to events" section below
  • Added extension methods for multiple classes related to brushes/colors
  • Added additional methods under the Tools class, including AddTextPathToGraphics which can be used to correctly position text on a key image based on the Text Settings in the Property Inspector see "Showing Title based on settings from Property Inspector" section below.
  • Additional error checking
  • Updated dependency packages to latest versions
  • Sample plugin now included in this project on Github

2019-11-17

  • Updated Install.bat (above) to newer version

Version 2.6 is out!

  • Added new MD5 functions in the Tools helper class
  • Optimized SetImage to not resubmit an image that was just posted to the device. Can be overridden with new property in Connection.SetImage() function.

streamdeck-tools's People

Contributors

barraider avatar dependabot-preview[bot] avatar jxyme 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

streamdeck-tools's Issues

NON ISSUE (QUESTION): Is there a way to SEND events with this?

I'm a software engineer who loves to automate everything. Specifically, I'm wanting to setup global hotkeys with a chording (or chaining) approach, like in Visual Studio - creating global hotkeys that require two subsequent hotkey presses. For example, in Visual Studio, to run all tests in the Test Explorer, you'd first press [Ctrl+R], then [A].

The problem isn't the chording; AutoHotkey (AHK), while crude, facilitates this behavior quite well.

Here's what I'm after:

A hotkey triggered in AHK would send a global event on the operating system, that MyPlugin : PluginBase would listen for. When that event is triggered, MyPlugin would send the switchToProfile event to the Stream Deck, switching the profile.

According to Elgato's documentation, you are able to send this event: switchToProfile:

https://developer.elgato.com/documentation/stream-deck/sdk/events-sent/#switchtoprofile

Is this possible with the library, as is?

If no - would you mind inviting me as a contributor so I can add this functionality and submit a PR for it?

Thanks!
NullPointerExpert (NPE)

Use of Framework 4.8.1

Just to report, that because of Barraider, the system was bugging me to install Framework 4.8.1 ,which seems not to be the default on all Windows 10 yet, and not needed for Stream Deck by Elegato.

Barraider seems the only Plugin that uses this Version, not sure if it is necessairy for the functionality.

However i had to remove the plugin for that reason.

Maybe you might consider downgrade to 4.8 if possible, since that is the version that 90% of all people have on their machines already.

Note: in some countries/windows versions therre might be already 4.8.1 active, but seems not everywhere.

Did you think about manifest auto generation

Every action change or add or removed always fix manifest is little complicated.
I have a idea to solve this problem by auto generate manifest from classes with attributes. For example

[PlugInActionId("com.blahblah")]
[ActionName("MyActionName")]
[Icon("...")]
[Tooltip("...")]
[State("Images/pluginState1","Top","12")]
[State("Images/pluginState2","Top","12")]
[PropertyInspectorPath("...")]
public class KeyAction:KeypadBase{
...
}

I dont know what is easiest method, Post build event run script or T4 template or something.

What you think?

Question regarding the possibility to implement unit testing

Hi, I have been using StreamDeck Tools for a while now. So first of all, thank you for this awesome wrapper ๐Ÿ™‚.

The more plugin I write, the more I to want to add unit/ tests to make sure everything is working as expected.
But the current implementation of the PluginBase class makes it hard to decouple the SDConnection class, such as making the SDConnection an interface in order to be able to inject a mocked SDConnection.

That would allow me to separate the logic of the plugin itself from the StreamDeck connection, pass custom plugin/device configuration, etc.

So I guess my question is that if you plan to implement this kind of changes in the short to medium time frame (if at all), or some kind of testing framework around it, or possible at all.

Thank you.

Edit: I went ahead, cloned the repository, and implemented ISDConnection interface. Built my plugins with it and so far they work fine. I'll be testing how useful it is for unit testing.

Text-alignment button Play/Pause

The text of the "Play/Pause" button cannot be aligned to the top.

For illustration: In the image below, the "03:08" should be aligned at the top (the remaining time is the only value to be displayed).

Spotify

Feature Request: Dials Call any Keys Command

This is likely impossible but Dials on The Stream Deck+ are pretty useless right now.... all it would take to make them super painful is a way for them to call any command from keys on press, turn left, turn right and make this user configurable. Not sure if cross calling plugins is possible?

i told you this software does not let me message you

i told you -
this software does not let me message you

this is the only way to message you on this software DO YOU - GET --- that ?

yes ?
all apologies - its a song by nirvana ...

"""did the teacher separate us ?? or did we separate ourselves """
this camera sucks so bad lol -------- i did art school and gifted and ...

IMG_20210227_194030_8

Conversion from NLog to MSL

Please look into changing from NLog to MSL.

The current wrapper implementation is very limited and Microsoft.Extensions.Logging allows for users to have greater flexibility with logging (including linking to other loggers). This means we can select the type of logging that is right for us, and utilise newer functions.

The current implementation of NLog within the wrapper doesn't support extra arguments, despite NLog itself supporting it, eg...

Logger.Instance
.LogMessage(
    TracingLevel.ERROR,
     "Action {FunctionName} failed due to error {ErrorName} ({ErrorCode:X8}).",
     funcName,
     Enum.GetName((_HResultDefs)hResult),
     hResult
);

This throws an error about overloaded arguments, and will not compile, when using the SdTools wrapper for NLog.

More specifically, MSL Abstractions is what we'd need implemented.

Insane amount of Windows Registry queries, is this normal?

I ran Process Monitor from Sysinternals for 30 minutes and noticed most of the catched entries came from BarRaider tools. They are continously reading from and writing to the Windows Registory folder HKLM\SOFTWARE\WOW6432Node\Microsoft\Cryptography\Defaults\Provider\Microsoft Enhanced RSA and AES Cryptographic Provider. I don't think it's a big deal security-wise, probably side effects from the .NET API's crypto functions.

But I am wondering about this insane amount. Your tools accessed the Windows Registry several hundreds of thousands of times in a timespan of some minutes, completely topping any other process on my PC. It's happening for multiple of your Stream Deck plugins, so this might be a streamdeck-tools thing and I'm fine opening an issue here. There are even entries for com.barraider.supermacro.exe and com.barraider.voicemeeter.exe, which I installed from the Elgato Store, but don't have any Stream Deck keys of.

I tried to upload the access log to GitHub Gist, but it was way too large, so I cut the recording down to 3 minutes, resulting in 48885 access entries from BarRaider apps.

https://gist.github.com/Jaid/ebdb76202ef8ca1589acb3bd5d999f82

Missing onAppear and onDisappear events

Hi,

I was happy to see that the StreamDeck events made available in the API, but the two most interesting, onAppear and onDisappear, are not included.
I would have used them to open and close a websocket connection to another server, that otherwise would not be initiated because I don't use the onTick action in favor of an event based trigger in this module.

No Project License

Hi @BarRaider, I wanted to ask you if you plan to add a license to the project.
By not having a license, we are not allowed to even include your library in our projects, which would defeat the purpose of the tool itself.

Thanks.

Custom icons not an existing file on disk - DistributionTool

Hi ! ๐Ÿ‘‹
I wanted to add custom icons to my plugin but when I run the .bat file, I get an error from the DistributionTool:
Error: The action 'my.action' has an "Icon" property with the value '[email protected]' which is not an existing file on disk. Please add a valid value to the property "Icon" in the manifest.json .
Capture-console-distributiontool

The icons are in the correct format (72x72 & 144x144 for @2x) and placed in the correct folder.

Capture-vs-manifest

I build, clean and regenerate the project. Maybe I forget some things ?

Thank for your reply.

synopsis of last request -- [sigh]

ok so that was not stream of conscious. yet. spent like 3 hours making sure i got all the details right.

  1. cannot make a button display without calling a key routine
  2. key routine takes a reference, the new "out" type - and then , thats just a draw thing
  3. returns a bitmap, which is dubious to out? usage? unless its because its static
  4. if you set the bitmap to null there is no button display
  5. all i see is that it takes a draw thing, which never exposes its original file load\image
  6. secondly that it sends back a bitmap with no attachment or association with anything specific
  7. unless this is a super-class that is inhereting another class, with transparent usage of the underlaying function
  8. i see it makes a new var, local to the function\class (static of course) - i mean that is local so - t.f.

i do not get this image handling thing ----------------- i am fairly sure i 80% get out - but i mean you only need 40 to use it

inconsistencies with comments and Don't Treat New Line As Enter

sorry if this isn't the right place to post this. I seem to have found a bug, or at least some unintuitive behaviour.

First off, the wiki says:

{{//}} | Comments Support: Anything after the {{//}} sign will be ignored until end of line.

I read this literally, and assume that comments can be placed after a {{//}}. Everything after {{//}} will be ignored, but only until the end of the line. So, I assume this should work correctly:

{{windows}}
{{//}}   blablabla
{{pause:500}}
{{escape}}

If Don't treat New Line as Enter is UNchecked, then it works. But if it is checked, then everything after the comment is ignored (ie. WIN is pressed but ESC is not). It seems to be treating everything after the comment as being part of the same line. This doesn't make sense to me. If I've checked that box, I don't want a new line to be sent as an actual [enter] keypress, but of course I still want SuperMacro to treat a New Line as a New Line.

On the other hand, if I place the comment within the curly brackets like this:

{{windows}}
{{//blablabla}}
{{pause:500}}
{{escape}}

then everything works, whether the checkbox is checked or not. This is how I've actually always done comments. But after reading the wiki entry above I switched to the {{//}} blablabla method, which is when I ran into problems.

Anyway, thanks again for a fantastic plugin. Elgato should pay you for how much better your plugins make the Stream Decks. I own 4 Stream Decks, and I can honestly say I wouldn't have bought the last 3 if your plugin didn't exist.

StreamDeck+ Unknown dialUp/dialDown event received

public override void DialPress(DialPressPayload payload) is triggered properly but I'm getting an Event dialUp and dialDown FATAL log line BarRaider.SdTools.Communication.StreamDeckConnection Unknown event received from Stream Deck.

Using StreamDeck+ device, StreamDeck software version 6.1.0.18521, and StreamDeckTools 6.0.0.0 (NuGet)

profile load method -

hey couple things here - and as "usual" working on it RIGHT now so it's like duhhh

OK

  1. found the profile load thing the comment says its for the buttons profile ???? tf is this
  2. it does not load the profile by the name i give, that does exist (stream deck profile vs button profile ??)
  3. afaik there is no way to get the button id or position from stream deck so if im in #1 at top left, it tells me ?
  4. so i'm trying to load in an entire profile, and then if i have to i can do this with MSI installer or however

let me know i got like 5 minutes ): before i start plugging it in whatever way i can at the moment ............................. sigh
"useless internet"

could also make fun of those people that say "quote unquote" first
like cancelling out their entire point ----------
quote unquote "sarcastic finger marks"

pretty sure you dont want to say unquote anywhere near anything you want someone to remember (8

hey so i am making my most best video for my most best babe

we are on the cusp of the next generation of personal computing - my friend

i got to post from google drive my girl will not get it - its 2 minutes of video

i want to include you in man
i want you to be a part of the future

but you got to take the plastic buttons at face value -- here

i LITERALLY CAN NOT WORK GOOGLE I HATE GOOGLE MAYBE IF I GET IT TEN MINUTES I GET YOU URL ???

the video is damn posted to my iphone toting girl
mayan girl i love the spaniards from mexico
aztec incan mayan ? educated? yes yes no adult swim no family guy
*i am the way

https://drive.google.com/file/d/1_HvWbhYff7kR7jwfmybLVfRaK9mciVUW/view?usp=sharing

Sponsored Feature Requests

Hi @BarRaider -
We are using WinTools for our business and love it.

We are interested in sponsoring some additional features/enhancements.

Are you open to that? If so, grab a meeting with me:
General Meeting

(I'm booked until next week).

multiple config file issue ?

is this even a support thing? i'm a base coder too so i figured this would be for that , and not "use"

-- regardless

OK multiple buttons here, the same way you did yours - its in one of your packages at least -
then the json - then inside that the mandatory html file ; and inside that - also input

i see on the submission that you appear to marshal , and i cant tell if you went left or right - and it doesn't concern me
i dont need to make that call right now honestly

the thing is - when these three buttons are loaded they all have the same exact contents to the HTML config
because there's no identifying factors, and the input name itself yes but that i can change and the behavior does NOT

wait i had to explain that first and i missed the problem - 2 of 3 buttons dont load the input
i went through it , and right after i click submit i am deleting the config of the one that works to see if it shifts to one of the other two

Add support for folders

Hi there,

I'm not sure if there is already something like that and I just couldn't find it.

I'm writing some plugins that should act as folders for other plugins.
I.e. the display is custom, but when pressing the button you should enter a folder.
Currently, I'm solving this with separate profiles, but this is very inconvenient.

Best regards,
Kc

AddTextPath doesn't dispose all objects, doesn't provide horizontal alignment, and vertical alignment doesn't work correctly.

Here is an updated version of this function that resolves these three items.

	/// <summary>
	/// Adds a text path to an existing Graphics object. Uses TitleParameters to emulate the Text settings in the Property Inspector.
    /// Fixes issues with some resources not being disposed at the completion of the call.
    /// Also fixes issues with verticle alignment and allows you to specify horizontal alignment.
	/// </summary>
	/// <param name="graphics"></param>
	/// <param name="titleParameters"></param>
	/// <param name="rectangle">Rectangle where you would like to place the text, can typically be the whole image size.</param>
	/// <param name="text"></param>
	/// <param name="strokeColor"></param>
	/// <param name="strokeThickness"></param>
	/// <param name="horizontalAlignment">Alow horizontal alignment like StreamDeck does natively</param>
	public static void AddTextPath(this Graphics graphics, TitleParameters titleParameters, Rectangle rectangle, string text, Color strokeColor, float strokeThickness, StringAlignment horizontalAlignment = StringAlignment.Center)
	{
		try
		{
			if (titleParameters == null)
			{
				Logger.Instance.LogMessage(TracingLevel.ERROR, "AddTextPath: titleParameters is null");
				return;
			}

			using (var font = new Font(titleParameters.FontFamily, (float)titleParameters.FontSizeInPixelsScaledToDefaultImage, titleParameters.FontStyle, GraphicsUnit.Pixel))
			{
				graphics.PageUnit = GraphicsUnit.Pixel;

				StringAlignment lineAlignment;
				switch (titleParameters.VerticalAlignment)
				{
					case TitleVerticalAlignment.Middle:
						lineAlignment = StringAlignment.Center;
						break;
					case TitleVerticalAlignment.Bottom:
						lineAlignment = StringAlignment.Far;
						break;
					default:
						lineAlignment = StringAlignment.Near;
                        break;

				};
                // Let StringFormat do the work to do the alignment
				var format = new StringFormat() { LineAlignment = lineAlignment, Alignment = horizontalAlignment };

				using (var graphicsPath = new GraphicsPath())
				{
					graphicsPath.AddString(text, font.FontFamily, (int)font.Style, graphics.DpiY * font.SizeInPoints / rectangle.Width, rectangle, format);

					using (var pen = new Pen(strokeColor, strokeThickness))
					{
						graphics.DrawPath(pen, graphicsPath);
						graphics.FillPath(new SolidBrush(titleParameters.TitleColor), graphicsPath);
					}
				}
			}
		}
		catch (Exception arg)
		{
			Logger.Instance.LogMessage(TracingLevel.ERROR, $"AddTextPath Exception {arg}");
		}
	}

Not understanding the StreamDeck-Tools Template tool

It downloads as a Visual Studio extension but I can't find any info on how to use it? Is there a menu in VS for it?

I am also having issues getting the install.bat to work. I am attempting to use it on your stopwatch example. Steps

  1. Place install.bat in bin folder.
  2. Created folder C:/TEMP/
  3. Edit install.bat with correct paths for DistrobutionTool, etc.
  4. Run install.bat RELEASE com.barraider.stopwatch

Error: "C:\TEMP"\com.barraider.stopwatch.streamDeckPlugin' is not recognized as an internal or external command,
operable program or batch file.

SetTitleAsync no longer updates title of buttons

I have a working plugin that updates the images on the buttons just fine, but the text is not updated, even though the call to SetTitleAsync does not throw an exception.

Maybe something has changed with a newer sdk?

I will try to take one of your example plugins and make some minor changes to it as a reproducible example.

Possible function request? Visualiser

Hey! Amazing plugin and really really helpful!

Possible function request - the ability to span it over multiple buttons? for example, splitting the EQ over 3 buttons wide?

Tried to find an email to send a message but wasn't able to find one. I'd even pay for this functionality!

Matt

Conversion from NSJ to STJ

Please look into converting from Newtonsoft.Json to System.Text.Json

System.Text.Json is a more strictly-typed Json implementation, however, where we need more flexible syntax, flags are available to support this. However, it will encourage developers to write strictly-typed Json (e.g. where we need a numerical value in the Json, code provided below)...

"SDKVersion": 2

Whereas this would be parsed as a string

"MinimumVersion": "6.5"

(except where override flags are set)

The situation is that Newtonsoft is basically EOL, and isn't receiving ongoing support. System.Text.Json is actively maintained.

Virus?

Full log log.txt

Malwarebytes
www.malwarebytes.com

-Log Details-
Scan Date: 6/26/20
Scan Time: 2:39 AM
Log File: fca54a70-b790-11ea-8bb9-dcf5050d6df8.json

-Software Information-
Version: 4.1.0.56
Components Version: 1.0.955
Update Package Version: 1.0.26041
License: Premium

-System Information-
OS: Windows 10 (Build 19041.329)
CPU: x64
File System: NTFS
User: System
-Scan Options-
Memory: Enabled
Startup: Enabled
Filesystem: Enabled
Archives: Enabled
Rootkits: Disabled
Heuristics: Enabled
PUP: Detect
PUM: Detect

-Scan Details-
Process: 1
Malware.Generic.4171343795, C:\USERS@@@@@\APPDATA\ROAMING\ELGATO\STREAMDECK\PLUGINS\COM.BARRAIDER.WINTOOLS.SDPLUGIN\COM.BARRAIDER.WINTOOLS.EXE, No Action By User, 1000000, 0, , , ,

Module: 1
Malware.Generic.4171343795, C:\USERS@@@@\APPDATA\ROAMING\ELGATO\STREAMDECK\PLUGINS\COM.BARRAIDER.WINTOOLS.SDPLUGIN\COM.BARRAIDER.WINTOOLS.EXE, No Action By User, 1000000, 0, , , ,

File: 2
Malware.Generic.4171343795, C:\USERS@@@@@\APPDATA\ROAMING\ELGATO\STREAMDECK\PLUGINS\COM.BARRAIDER.WINTOOLS.SDPLUGIN\COM.BARRAIDER.WINTOOLS.EXE, No Action By User, 1000000, 0, 1.0.26041, 000C5276FE1DEE47F8A1A7B3, dds, 00780740
Malware.Generic.4171343795, C:\USERS\NRRINC\APPDATA\LOCAL\TEMP\COM.BARRAIDER.WINTOOLS.STREAMDECKPLUGIN, No Action By User, 1000000, 0, 1.0.26041, 000C5276FE1DEE47F8A1A7B3, dds, 00780740

Cannot change title of Scene Switcher button

Running Streamdeck v6.1.0.18521 with OBS 29.9.0.

Cannot change the Title of Scene Switcher buttons. When I add a new Scene Switcher button, the Title field says "disabled" and I cannot change it. The buttons work, I just cannot re-label them. Installed latest release of the plugin, but did not fix. Any help would be appreciated.

SharedScreenshot

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.