Giter Site home page Giter Site logo

tidalcycles / strudel Goto Github PK

View Code? Open in Web Editor NEW
551.0 9.0 102.0 156.02 MB

Web-based environment for live coding algorithmic patterns, incorporating a faithful port of TidalCycles to JavaScript

Home Page: https://strudel.cc/

License: GNU Affero General Public License v3.0

HTML 3.79% JavaScript 64.28% CSS 0.49% PEG.js 1.24% Python 0.02% Shell 0.03% TeX 4.73% TypeScript 1.13% Csound 3.91% Csound Document 0.01% Makefile 0.04% Astro 1.81% MDX 17.78% Rust 0.74%
algorave tidalcycles tidal algorithmic-patterns livecoding javascript music web-audio webaudio synthesizer

strudel's People

Contributors

alexanderhollauf avatar atfornes avatar bpow avatar bubobubobubobubo avatar bwagner avatar charlieroberts avatar chiakiuehira avatar daslyfe avatar dsm0 avatar eefano avatar felixroos avatar geikha avatar gogins avatar ijc8 avatar ilesinge avatar jarmitage avatar kasparsj avatar mindofmatthew avatar mystery-house avatar oscarbyrne avatar paikwiki avatar puria avatar rjulian avatar roipoussiere avatar shiyouganai avatar srenatus avatar stephendwolff avatar urswilke avatar vasilymilovidov avatar yaxu 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

strudel's Issues

Switching between tabs makes Strudel unresponsive

The bug was found using Google Chrome (latest version) on a MacBook Pro 13" (latest OS). In order to reproduce it, open Strudel in one tab and two or three other random tabs (like the ToneJS API doc and a few google queries). Switch from Strudel to documentation while running a moderately complex / dense pattern. After switching back by alt-tabbing, Strudel REPL stops rendering sound and appears to be loading for an undefinite time. It can sometimes resume its course or, in other cases, crash.

Browser plugins: Vimium, Dark Reader, uBlock Origin.
Chrome version: 101.0.4951.54
MacOS version: 12.2.1 (21D62)

I don't know a lot about web browsers but I wonder if this could be linked to a browser extension altering Strudel behavior somehow. It could also be because my patterns were making extensive use of ToneJS, but nothing fancy really (monosynth with custom parameters).

Happy to help to investigate this issue by running tests or by looking at page performance :).

[osc] mysterious clock drifting

Under some unknown circumstances, the osc messages will arrive late in supercollider.
On lowend machines, this seems to happen more easily, though it could be a separate problem.
There seems to be some clock drifting going on, most likely that osc uses the system clock and strudel uses Date.now (on page load) + audio time since since page load. So far, I was only able to get late messages after a longer time of inactivity without refreshing the repl. after a refresh, the clocks are aligned again.

The clock drifting could be avoided if .osc would use performance.now instead of audio time.

Resolve 'osc' confusion

There's pattern.osc() for sending via OSC protocol, and osc() for setting an oscillator shape. It'd be good to differentiate them somehow.

Fix legato and duration

The current legato implementation manipulates haps in a way that can lose data.

A fix would be to copy tidal's behaviour - store legato in a normal value key and then deal with it in the scheduler.

find out why `@strudel.cycles/eval` fails with skypack

Opening a html file with the following contents:

<script type="module">
  import { evaluate } from 'https://cdn.skypack.dev/@strudel.cycles/eval';
</script>

throws this error:

shift-parser.js:4857 Uncaught TypeError: Super expression must either be null or a function, not undefined
    at _inherits (shift-parser.js:4857:13)
    at shift-parser.js:4931:5
    at shift-parser.js:5690:4
    at createCommonjsModule (shift-parser.js:15:6)
    at shift-parser.js:4785:19

This seems to be an issue with shift-parser. The error can also be produced by:

<script type="module">
  import { parseScriptWithLocation } from 'https://cdn.skypack.dev/shift-parser';
</script>

Missing documentation

There's probably quite a few things missing at this point, lets make a list:

core

  • pure
  • cat
  • seq
  • stack
  • timeCat
  • slow
  • fast
  • early
  • late
  • legato
  • struct
  • euclid
  • euclidLegato
  • rev
  • iter
  • iterback
  • every
  • each TODO: remove and adjust every
  • when
  • .stack
  • .superimpose
  • .layer
  • .off
  • echoWith
  • echo
  • .seq
  • .cat
  • .add TODO: move to in-source doc
  • .sub TODO: move to in-source doc
  • .mul TODO: move to in-source doc
  • .div TODO: move to in-source doc
  • round
  • add
  • sub
  • mul
  • div
  • floor
  • ceil
  • apply
  • range
  • range2
  • rangex
  • chunk
  • chunkBack
  • fastGap
  • ply
  • chop
  • cpm
  • zoom
  • zoomArc
  • linger
  • color
  • segment
  • invert / inv
  • when
  • append
  • palindrome
  • juxBy
  • jux
  • edit (deleted)
  • bypass (same as hush?)
  • hush
  • duration (deprecated?)
  • legato
  • velocity
  • speak (experimental)
  • draw (deprecated)
  • pianoroll (@jarmitage)
  • restart
  • reset

signal

  • steady
  • signal
  • isaw
  • saw
  • sine
  • cosine
  • square
  • tri
  • rand
  • irand
  • chooseWith
  • chooseInWith
  • time
  • brand, brandBy
  • wchooseWith
  • wchooseCycles
  • perlinWith
  • perlin
  • degradeByWith
  • degradeBy
  • degrade
  • undegradeBy
  • undegrade
  • sometimesBy
  • sometimesByPre ?
  • sometimes
  • sometimesPre ?
  • someCyclesBy
  • often
  • rarely
  • almostNever
  • almostAlways
  • never
  • always

webaudio

  • s
  • attack decay sustain release
  • samples
  • clip
  • gain
  • cutoff / resonance
  • hcutoff / hresonance
  • bandf / bandq
  • vowel
  • pan
  • coarse
  • shape
  • crush
  • freq
  • speed
  • begin / end
  • delay delayfeedback delaytime
  • unit (doesn't do much atm)
  • nudge
  • cut
  • loop
  • loopAt
  • orbit
  • room / roomsize
  • soundfont (experimental)

tonal

  • transpose
  • scale
  • scaleTranspose
  • voicings
  • rootNotes

midi

  • midi
  • midi second param #192

osc

  • osc

xen

more

  • pianoroll (@jarmitage )
  • spiral
  • theme
  • fontFamily
  • fontSize

๐Ÿ’–

Woke up this morning, I was looking on how vortex was simple... and thought why Qt when I can write the nicest editor ever in js/ts.
Then I init my new project (I am actually experimenting with https://github.com/MylesBorins/node-osc to talk to superdirt) when I saw the notification of this repo ;p coincidences ๐Ÿฅณ

collaborative editing

I think would be interesting considering an additional package to the structure that provides real-time audio and data communication. A typical use case would be a session of multiple players collaborating on the same session.

With this application in mind, I think the library should allow a user to initiate a public session and invite other players using the generated link. This means that the library would be in charge of defining a session and return a URL invitation.

The negotiation between peers can happen without a server, despite of the topology. There are few libraries that makes this process very easy. They take over most of the required coordination to instantiate a p2p stream, reducing a lot the amount of code needed to define a real-time session.

Suggested: https://github.com/peers/peerjs

make curried function composable

Curried functions are currently not composable:

this works:

"c3".superimpose(
  add(3),
  add(7),
)

this doesn't work:

"c3".superimpose(
  add(3).velocity(.5),
  add(7),
)

error: add(...).velocity is not a function

this works:

"c3".superimpose(
  x => x.add(3).velocity(.5),
  add(7),
)

ideally, all curried functions need to have all pattern methods.
This is what makeComposable is supposed to do, but it does not seem to work

Perf on repl

Ciao ;)
<3 you are doing a super great job!

I was looking at the perf, a little bit, and before there were a lot of repaintings while the notes where highlighted (maybe some useMemo, may help) and also find out:

Screenshot 2022-02-27 at 16 39 50

That make a slow function call (each cycle?), maybe now that the codebase is small, could be narrowed down easier...

I wish I had some time, just those extra two hours during the day, ahahah if you think can help, I can investigate more

Standardise representation of values

Bring in unused value.mjs, and put all data in the value as an object, leaving context for things independent of value, like source code position.

Make license clear

We should have a copyright notice clear in the source code of https://strudel.tidalcycles.org/ etc, and I think via a statement or link on the page as well.

The GPL also recommends putting a copyright, license and warranty disclaimer notice at the top of every source file. We could do this like:

/*
filename.mjs - short description
Copyright (C) 2022  xxx and contributors

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. https://www.gnu.org/licenses/gpl-3.0.en.html
*/

The recommended version is a bit longer, but perhaps this is a good compromise..

If we make any servers they should probably be under the Affero GPL.

test suite for all tunes

It would be great to have a test suite that tests each tune in tune.mjs (which are used as random tune in repl).
At least, we should ensure that no error is thrown, even better would be to snapshot the first x cycles, to make sure everything stays consistent

binary pattern reset function

I don't know if this exists in tidal, but it would be fabulous if there was something like that:

"c3 e3".reset("<x [x x]>")

being equivalent to "<[c3 e3] [c3 c3]>". So the binary pattern will reset the input pattern each time there is a true event.

In the example, this is looks pretty boring, but for composing, this opens many doors, for example making sure the bass lines always lands on the root when the chord / scale changes.

Here is an example of a bass line that doesn't land on the 1 for the last note:

"0 4".scale(
  sequence('C major', ['D dorian', 'G mixolydian']).slow(2)
)

The output is equivalent to "<[c3 g3] [d3 d4]>" with the indices "<[0 4] [0 4]>".

Ideally, the last note should be a g3, index 0 in 'G mixolydian' which starts there.
A human bass player would almost always play the root note on the first beat of a new scale / chord.

To mimic that behavior, we could use the not yet existing reset function:

const scales = sequence('C major', ['D dorian', 'G mixolydian']).slow(2)
"0 4".reset(scales).scale(scales)

This will reset the "0 4" each time the scale changes (any scale is true), creating this index pattern

"<[c3 g3] [d3 g3]>"
// "<[0 4] [0 0]>"

... which is what we want in that case. This is not only useful for basslines, but also arpeggios or glitchy retriggering patterns.
This will be fantastic for any type of dynamic groove!

add simpler version of Pattern.query

this is bulky: pattern.query(new State(new TimeSpan(0, 1))). It would be cool to have a way to query by just passing numbers. One of:

  • pattern.query(0, 1) <- this would require "overloading" the function by type checking
  • pattern.queryArc(0, 1) <- this would just be plugging .query(new State(new TimeSpan(begin, end)))

pass webaudio nodes to webdirt graph

it would be nice if there was a way to take any webaudio node, like an oscillator, and pass it to webdirt in some way. maybe this needs to be implemented on the webdirt side. it would enable to tun any web audio node though webdirts effects (and not just samples).
if this works, we can rewire the existing non webdirt web audio stuff to that method + standardize stuff (.wave('sawtooth') => .s('sawtooth') etc)

Mini-notation for fraction shorthands

The tidal mininotation has these shorthands for fractions:

'w' 1
'h' 0.5
'q' 0.25
'e' 0.125
's' 0.0625
't' (1/3)
'f' 0.2
'x' (1/6)

It allows a prefix factor, e.g. 5s is parsed as 5/16ths, 2t as 2/3rds etc.

e on its own would be 1/8th, unlike for notes where it would be parsed as the note label for 4.. I.e. notes are parsed differently from other numbers.

Explicit ratios can be given with %, e.g. 3%7 would parse to 3/7. (/ can't be used as that's taken by 'slowing down' steps).

Part of #30.

automate repl / tutorial build

currently, building the repl must be done manually. it would be cool if each push to main would trigger a rebuild.
the repl and the tutorial can be built with cd repl && npm run build. currently this will move the build to /docs folder, but it could be smarter to use gh-pages branch instead.

This would also automatically update the tutorial when people make edits from github directly, which is nice

Glicol Bindings

While Tone.js has cool instruments and effects, it has some performance problems, especially when trying to pattern sound params.

EDIT: Let's use this issue for discussion about glicol integration. General discussion about sound engines here #64

implement mainline tidal functions

There are many functions from tidal that have not landed yet.

Basics

  • drawLine #90
  • setcps #51
  • silence
  • hush

Needs longer term work/thought

  • naming patterns? block based evaluation? #34
  • once
  • panic
  • lindenmeyer
  • select, selectF, pickF (?) (need to decide on names)

Low priority or won't implement

  • wedge
  • flatpat
  • <> operator (overlay/stack)
  • steps
  • stripe, slowstripe
  • step / step'
  • spread / spreadf / fastspread

Concatenation

https://tidalcycles.org/docs/patternlib/tour/concatenation

  • cat: is synonym to fastcat in strudel, while in tidal, cat is slowcat
  • fastcat
  • timeCat: why is this camel case?
  • randcat
  • append: not implemented, as made redundant by cat #89
  • fastAppend: ditto with fastcat
  • slowAppend: ditto with slowcat
  • brak

Accumulation

  • overlay => removed, as it's the same as stack see #89
  • stack
  • superimpose
  • layer
  • iter
  • iter' = iterBack

Alteration

  • range, rangex
  • quantise
  • ply
  • stutter = echo
  • palindrome = every(2, rev)
  • trunc
  • linger
  • chunk, chunk'
  • shuffle
  • scramble
  • rot
  • spreadChoose / spreadr

conditions

  • every
  • every' - what's this?
  • whenmod - strange, but people do use it.. #618
  • sometimes, sometimesBy, someCycles, someCyclesBy
  • choose, chooseby, wchoose, wchooseby - wchooseBy named wchooseWith in strudel
  • struct
  • mask
  • sew
  • stitch
  • squeeze
  • euclid, euclidLegato
  • euclidInv, euclidFull
  • ifp - what's this?

Time

  • fast
  • fastGap
  • slow
  • hurry
  • compress: is this compressSpan?
  • zoom
  • within #1039
  • off
  • rotL / rotR - renamed 'early' and 'late' (in tidal these are usually used in their operator forms <~ and ~>
  • rev
  • jux
  • juxBy
  • swingBy / swing #1038
  • ghost
  • inside / outside

Harmony & Melody

Think these are just implemented differently in strudel?

  • scale
  • scaleList
  • getScale
  • toScale
  • chordList
  • arpeggiate
  • arp

Transitions

these need block-based eval

  • anticipate / anticipateIn
  • clutch / clutchIn
  • histpan
  • interpolate / interpolateIn
  • jump / jumpIn / jumpIn' / jumpMod
  • wait / waitT
  • wash / washIn
  • xfade / xfadeIn

Sampling

  • chop
  • striate
  • striateBy
  • loopAt
  • segment
  • discretise - old name for segment.. lets just stick with the new one
  • slice
  • splice

Randomness

  • rand / irand
  • perlin / perlinWith
  • perlin2 / perlin2With

Composition

  • ur
  • seqP / seqPLoop

Split into separate packages

Should these be separate repos? I can imagine other projects (gibber, hydra etc.) wanting to use the library...

Embedding strudel

Discussed in #103

Originally posted by yaxu April 29, 2022
It would be nice to be able to easily add working strudel code to e.g. a discourse forum (like https://club.tidalcycles.org) or wordpress blog via a plugin. Hope no-one minds me capturing some ideas from the discord chat:

  • create web component
  • adjust style of repl if embedded
  • add discourse plugin? or is there an easier way to whitelist web components?

visual cues

  • flash code on evaluation #144
  • more obvious loading state for async patterns, e.g. piano can load a bit longer #542
  • floating labels (ctr+enter, error messages) should be fixed and not absolute so they are always visible
  • more visible cursor? https://discuss.codemirror.net/t/how-to-set-text-cursor-caret-width/4430
  • block cursor (#126 (comment))
  • location of errors should be marked (though this might be tricky, as the locations are in the context of the shapeshifted code. edit: would be fixed with #174)

rework voicings

use full potential of chord-voicings

  1. change voicings(range) to patternified voicings(type) , where type is a voicing dictionary name
  2. add patternified voicingRange(min, max)
  3. add more voicing dictionaries! lefthand voicings get boring quickly (maybe a task for chord-voicings repo)
  4. allow passing custom dictionaries

add missing mini notation features

Compared to mainline tidal, the following features are missing from the mini notation:

  • Tie symbols "_" #926
  • feet marking "." #926
  • standalone "!" #926
  • random choice "|" #165
  • Random removal "?" #165
  • Polymetric sequences "{ ... }" #336
  • Fixed steps using "%"
  • #65
  • #72
  • #176
  • colons
  • c3(3,8)? does not work ([c4(3,8)]? does)
  • polyrhythms with <> e.g. <bd hh, ht lt mt> #886
  • range operator "0 .. 6"` #716

For most of them to actually work, the corresponding functions need to be implemented in core.

block based evaluation

Think about if we should implement block based evaluation, like in mainline tidal.
Currently, the whole code is reevaluated when hitting ctrl+enter.
To make that work, some way of naming patterns needs to be found. One idea is using the rarely used javascript label statements:

d1: s("bd sn")

d1: s("bd ~")

If we run the fist line, and then the second, only the second should play.

To make that work, the runtime needs to store patterns in some map that stores { [name]: pattern }.
When a line is evaluated, the pattern map is updated.
After each evaluation all patterns in the map are stacked together and queried.

One question is when non pattern code should be evaluated? Example:

const p = "x ~ x*2"

d1: "c3 e3 g3".struct(p)

Here, d1 references p, so we need to have that evaluated too.
It would make usage much more complicated if the user is expected to run the first line before the second otherwise it crashes.

pianoroll() options

For example:

.pianoroll(axis, offset, direction, cycles) where:

  • axis is e.g. 0 or 1 corresponding to horizontal and vertical orientation (default 0)
  • offset a float from 0-1 will move now along some % of the selected axis (default 0.5?)
  • direction will change the scroll direction from left/right in horizontal and up/down in vertical orientations (default left-to-right)
  • cycles is an integer representing the number of cycles to display across the window (default 8?) (maybe this needs an upper limit for performance reasons?)

This is basically inspired by Hydra. So another approach altogether would be to send pianoroll() into Hydra, and then the above wouldn't be necessary (apart from cycles).

musical ties

It would be great if the mini notation somehow supported ties like this:

"[c3 e3] t"

"t" is just a placeholder here for a symbol that we haven't yet chosen. This would then have the output as:

sequence(['c3', pure('e3').legato(3)], silence)

without that, writing syncopations is really clumsy:

This works, but it's hard to read and you have to pack 2 "bars" into one package, at least if you want to do that inside a larger context. Here is a more real world example:

https://tinyurl.id/hTySd

I am pretty sure this feature cannot be implemented with patterns, as we have 2 (or more) seperate events that depend on each other. So the "e3" in the example has to "know" that the "t" is coming after it to have the proper duration. But this is impossible because the "e3" could probably be scheduled before the t.

TLDR to make this work, the tie notation needs to be converted to the clumsy notation before constructing the pattern.
For me, it would be the most logical to just use "_", but this would be a deviation from how it works in tidal for some cases.
Maybe "=".. ?

squeeze fragments

In this the 60/2 results in events cut in half, so resulting events do not have onsets (and so aren't played) every other cycle:

"40 50 60/2".addSqueeze("0 5")

Same with 5/2 here:

"40 50 60".addSqueeze("0 5/2")

I think the intersection of these events should be used as wholes, and not sure why they, aren't as _squeezeJoin makes use of intersection.

This doesn't create fragments:

"40 50 60".ply("3/2")

So it seems that it's something about _opSqueeze that's causing this behaviour.

server side scheduling

as requested by @treethought it would be handy if the server could evaluate and schedule patterns. this would allow using strudel with other editors, communicating via websockets.

  1. external editor sends user code to server via websocket
  2. server evaluates code
  3. server schedules active pattern and triggers osc messages (could work with midi too)

it could also be helpful if the server also sent scheduling messages via websocket to the client, allowing external playback or highlighting.

we could even try to use https://www.elementary.audio/docs/packages/node-renderer and render audio on the server

as @yaxu pointed out, it would be handy to have a standalone scheduler that works in the browser as well as in node, which should also support cps . vortex implementation: https://github.com/tidalcycles/vortex/blob/c575fa595d8cf537a7b59815a2e3dcdc14eec351/py-vortex/py_vortex/stream.py#L91

related #51

use webaudio scheduler in REPL

To be more independent, the tone.js scheduler in the REPL should be replaced.
In #26 , I implemented a scheduler that queries a pattern directly using a worker with a setInterval.
Though the exact moment of the setInterval callback is not accurate, it can still be used to query the active pattern, giving events for the current slice of time. Each event time can then be scheduled accurately using either bare web audio nodes or any other sound engine (#64).

read more

repl as package

move repl hooks + MiniRepl to installable package. This would simplify embedding strudel inside any webpage.

Add `squeezeJoin` / `squeezeBind`

For combining two patterns flexibly, strudel has a family of joins and binds that take the 'whole' timespans from one of the patterns (inner/outer) or the intersection of both. A (so far) missing option is Tidal's squeezeBind, which fits a cycle worth of events from one of the patterns into the whole timespan of the other. Implementing this will allow tidal functions like ply, press, chew, ascii, fit, chop and inhabit to be easily implemented (contributing to #31).

unified build

@mindofmatthew pointed out we could unify the repl and the tutorial build to one pipeline. currently, the repl is built with create-react-app (webpack), and the tutorial is built with parcel. We could build both with one tool, assuming it supports mdx and react.

One thing to keep in mind when picking a build system is that we might need to be able to set CORS headers, when we want to use a sound engine that relies on SharedArrayBuffer, see #29 (comment) and https://github.com/rd--/jssc3/blob/main/md/notes.md#cross-origin-isolation

also related: #43

Negative time is broken

We sometimes go into negative time, for example using .late. There are currently problems with dropped events, e.g.:

"50 60 70".ply(2).late(8)

I think this case is a problem with the comparisons in _compress but there may well be similar problems elsewhere.

append is redundant

Because strudel has varargs, cat can do everything that append does. Similarly stack does everything that overlay does in tidal.

Do we make append and overlay aliases of cat and stack? Or just not have them in strudel at all?

Patternable tempo changes via `cps`

There is cpm in core but it assumes a scheduler running at 1 cps.

It would be great if cps could be patternable, e.g.

cps(saw.range(0.9,1.8).slow(16))

or

sound("bd [bd bd]").cps(1,1.5)

event > hap

Events are called 'haps' in strudel, mainly because the word event is already taken.

However we still call them events in secondary notation (variable/function names and comments).

We should probably do a (careful) search and replace on everything. Or is it OK to use the terms interchangeably?

cannot evaluate after undo

codemirror6 does some thing that won't let evaluating after undo:

  1. enter some valid code
  2. evaluate
  3. make a change
  4. evaluate
  5. undo
  6. evaluate => won't work

this is pretty irritating

allow notes without octaves

in tidal, it's possible to write "c a f e" which creates notes that default to a certain octave, maybe 3.

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.