├── .editorconfig ├── .gitignore ├── LICENSE.md ├── README.md ├── homework ├── 00-intro-hw.md ├── 01-node-hw.md ├── 02-heroku-hw.md ├── 03-mongo-hw.md ├── 04-raspberry-hw.md ├── 05-midterm-hw.md ├── 07-audio-hw.md ├── 08-video-hw.md ├── 09-language-hw.md ├── 10-learning-hw.md ├── final.md └── midterm.md ├── lessons ├── 00-intro │ ├── 00-intro.md │ └── img │ │ ├── LICENSE.md │ │ ├── atm.jpg │ │ ├── bank-teller.jpg │ │ ├── container.jpg │ │ ├── harmonic_table.png │ │ ├── notes.png │ │ ├── spectrogram.png │ │ └── waveform_visual.png ├── 01-node │ ├── 01-node.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── server.js │ ├── src │ │ ├── namer.js │ │ └── os-info.js │ └── templates │ │ ├── changename.html │ │ ├── identity.html │ │ ├── index.html │ │ └── nameset.html ├── 02-heroku │ ├── .babelrc │ ├── .gitignore │ ├── 02-heroku.md │ ├── img │ │ ├── 01-template.png │ │ ├── 02-clone-mode.png │ │ ├── 03-hello.png │ │ └── 04-oopsie.png │ ├── package-lock.json │ ├── package.json │ ├── server │ │ └── index.js │ └── src │ │ ├── App.jsx │ │ ├── ServerInfo.jsx │ │ └── Welcome.jsx ├── 03-persistence │ ├── 03-persistence.md │ ├── Procfile │ ├── img │ │ ├── mlab-01.png │ │ ├── mlab-02.png │ │ ├── postman-json-body.png │ │ ├── postman-json-headers.png │ │ ├── smocking.jpg │ │ ├── webapp-diagram.ai │ │ └── webapp-diagram.png │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── json │ │ ├── app.js │ │ └── data.json │ │ ├── mongo-basic │ │ └── app.js │ │ └── trivial │ │ └── server.js ├── 04-raspberry │ ├── 04-raspberry.md │ ├── code │ │ ├── .gitignore │ │ ├── index.js │ │ ├── logging-test.js │ │ ├── package-lock.json │ │ ├── package.json │ │ └── yarn-error.log │ ├── hw │ │ ├── get_temp_range.js │ │ ├── make_test_data.js │ │ ├── package-lock.json │ │ ├── package.json │ │ └── test.log │ └── img │ │ ├── circuit.jpg │ │ ├── hostname-config.png │ │ └── installing-os-part1.png ├── 05-midterm │ └── 05-midterm.md ├── 06-osc │ ├── 06-osc.md │ ├── img │ │ ├── appkettle.png │ │ ├── basic-envelope.png │ │ ├── osc-controlling-sequencer.png │ │ ├── osc-multiple-parameters.png │ │ ├── osc-trigger-env.png │ │ ├── oscillator.png │ │ ├── processing-osc.png │ │ ├── sharks.jpg │ │ ├── squares.png │ │ └── unimpressive.png │ ├── patcher │ │ ├── silly-dumb-node-thing │ │ │ ├── .gitignore │ │ │ ├── code │ │ │ │ └── silly-dumb.js │ │ │ ├── patchers │ │ │ │ └── silly-dumb-node-thing.maxpat │ │ │ └── silly-dumb-node-thing.maxproj │ │ └── simple-beap.maxpat │ ├── processing │ │ └── patatap_osc │ │ │ └── patatap_osc.pde │ └── server │ │ ├── index.js │ │ ├── package-lock.json │ │ └── package.json ├── 07-audio │ ├── 07-audio.md │ ├── img │ │ ├── attribution.txt │ │ ├── audio-context-warning.png │ │ └── spectrogram_of_violin.png │ ├── meyda-app │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── Procfile │ │ ├── README.md │ │ ├── app │ │ │ ├── app.js │ │ │ └── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ │ └── style.css │ │ ├── server.js │ │ ├── webpack.config.js │ │ └── webpack.dev.config.js │ ├── meyda-demo │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── Procfile │ │ ├── README.md │ │ ├── app │ │ │ ├── app.jsx │ │ │ ├── components │ │ │ │ ├── ClockFace.jsx │ │ │ │ └── RootComponent.jsx │ │ │ └── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ │ └── style.css │ │ ├── server.js │ │ ├── webpack.config.js │ │ └── webpack.dev.config.js │ └── tonetest │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── Procfile │ │ ├── README.md │ │ ├── app │ │ ├── app.jsx │ │ ├── components │ │ │ ├── ClockFace.jsx │ │ │ └── RootComponent.jsx │ │ └── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ └── style.css │ │ ├── server.js │ │ ├── webpack.config.js │ │ └── webpack.dev.config.js ├── 08-video │ ├── 08-video.md │ ├── flow-app │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── Procfile │ │ ├── README.md │ │ ├── app │ │ │ ├── app.js │ │ │ ├── flow.js │ │ │ └── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ │ └── style.css │ │ ├── server.js │ │ ├── webpack.config.js │ │ └── webpack.dev.config.js │ ├── img │ │ ├── attribution.txt │ │ ├── illusion.jpg │ │ ├── kyle-files.png │ │ ├── smile.png │ │ └── wave.png │ └── ml5-app │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── Procfile │ │ ├── README.md │ │ ├── app │ │ ├── app.js │ │ └── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ └── style.css │ │ ├── server.js │ │ ├── webpack.config.js │ │ └── webpack.dev.config.js ├── 09-language │ ├── 09-language.md │ └── poem-docu │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── Procfile │ │ ├── README.md │ │ ├── app │ │ ├── app.jsx │ │ ├── components │ │ │ ├── ClockFace.jsx │ │ │ ├── PoemDisplay.jsx │ │ │ └── RootComponent.jsx │ │ └── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── poem │ │ └── poem.js │ │ ├── public │ │ └── style.css │ │ ├── server.js │ │ ├── webpack.config.js │ │ └── webpack.dev.config.js ├── 10-learning │ ├── 10-learning.md │ ├── commander-test │ │ ├── myscript.js │ │ ├── package-lock.json │ │ └── package.json │ ├── img │ │ └── learning │ │ │ ├── learning.001.jpeg │ │ │ ├── learning.002.jpeg │ │ │ ├── learning.003.jpeg │ │ │ ├── learning.004.jpeg │ │ │ └── learning.005.jpeg │ └── teachable-machine-app │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── app │ │ ├── app.js │ │ ├── drawDetector │ │ │ └── drawDetectorApp.js │ │ ├── heartHands │ │ │ ├── heart.js │ │ │ └── heartHandsApp.js │ │ ├── imageDetector │ │ │ └── imageDetectorApp.js │ │ ├── index.html │ │ └── soundDetector │ │ │ └── soundDetectorApp.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ └── style.css │ │ ├── scripts │ │ └── imageDownloader.js │ │ ├── server.js │ │ ├── webpack.config.js │ │ └── webpack.dev.config.js ├── 11-advanced │ ├── 11-advanced.md │ └── game-app │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── Procfile │ │ ├── README.md │ │ ├── app │ │ ├── app.js │ │ ├── game.js │ │ ├── gameClient.js │ │ ├── index.html │ │ └── util.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ └── style.css │ │ ├── server.js │ │ ├── webpack.config.js │ │ └── webpack.dev.config.js ├── 13-gesture │ └── 13-gesture.md ├── 14-back4app-experiment │ ├── .gitignore │ ├── LICENSE.md │ ├── Procfile │ ├── README.md │ ├── app │ │ ├── app.jsx │ │ ├── components │ │ │ ├── ClockFace.jsx │ │ │ ├── DumbTwitter.jsx │ │ │ ├── DumbTwitterForm.jsx │ │ │ ├── DumbTwitterList.jsx │ │ │ └── RootComponent.jsx │ │ └── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── bundle.js.LICENSE.txt │ │ └── style.css │ ├── server.js │ ├── webpack.config.js │ └── webpack.dev.config.js └── TEMPLATE.md ├── links.md ├── makeup └── 00-intro.md ├── package.json ├── reading_list.md └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = tab 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [package.json] 10 | indent_style = space 11 | tab_width = 2 12 | 13 | [*.yaml] 14 | indent_style = space 15 | tab_width = 2 16 | 17 | [*.md] 18 | indent_style = space 19 | tab_width = 2 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.pdf 3 | -------------------------------------------------------------------------------- /homework/00-intro-hw.md: -------------------------------------------------------------------------------- 1 | # Homework 00 2 | 3 | ## Due Date 4 | This assignment follows the first lesson, [00-intro](../lessons/00-intro/00-intro.md). There is a written portion! It must be turned in by midnight the day before class. You can turn in the assignment by sending it to me in an email attachment, or by sending me a link to a github repository. 5 | 6 | ## Assignment 7 | ### Reading 8 | - Aron Z. Lewis, _Inside the Digital Sensorium_, https://aaronzlewis.com/blog/2021/01/17/inside-the-digital-sensorium/ 9 | 10 | The very first thing we do in the second class will be to discuss this reading. The article isn't long, and I recommend reading it twice if you can, once after class and once again before we meet the second time. Some things that you might want to think about: 11 | 12 | - Which of the eight short stories do you identify with most? 13 | - Do you think a website is like a physical place? How do you feel about comparing a website to a sense organ? 14 | - What are the parts of this article that make you feel hopeful? What parts make you uncomfortable? 15 | - What is Instagram? An app? A product? A company? A collection of images and videos? All of these? None of them? 16 | 17 | If you want to make sure you're prepared for discussion, a nice technique is QCQ - Quote, Comment, Question. Make sure that you write down one quote from the reading, that you have some comment on it, and that you have a question about it. 18 | 19 | ### Writing 20 | In addition to the reading assignment this week, there will be a brief writing assignment. I want you to choose a piece of networked art, and to tell me how it works. Your interpretation of networked art can be as broad as you like, but it should be a piece that has multiple, distributed, coordinated parts. Similar to the breakdown we did in class, you should be able to answer: 21 | 22 | - What are the major parts of the system? What are their responsibilities? 23 | - How does the behavior of the system change if one of the parts is non-responsive? 24 | - What are the hardware and software components of each part? Can you make any specific guesses? 25 | - How do the parts communicate? What protocols do they use? 26 | 27 | Here's a list of some networked art pieces you can use: 28 | - David Bowen: https://www.dwbowen.com/telepresentwater — Tele-present water 29 | - Collective Of Two: https://vimeo.com/386776425 — Invisible Hand 30 | - eCLOUD: http://www.ecloudproject.com/ 31 | - The Living: http://cheraudesir.com/the-living-twin-mirror-2017/ - Twin Mirror 32 | - Ronin Tool: https://100r.co/site/ronin.html 33 | - The Living: https://architizer.com/projects/living-light/ - Living Light 34 | - Matt Roberts: https://vimeo.com/20500963 - Waves 35 | - Matt Kenyon: https://www.swamp.nu/projects/spore - Spore 1.1 36 | - Lots more here https://www.postscapes.com/networked-art/ 37 | 38 | ### Extra Reading 39 | 40 | If you're relatively unfamiliar with Node, Git, or JavaScript itself, I *highly* recommend brushing up before coming to class on Wednesday. 41 | 42 | #### Git 43 | - http://marklodato.github.io/visual-git-guide/index-en.html 44 | - https://rogerdudler.github.io/git-guide/ 45 | 46 | #### Node 47 | - https://medium.com/@jesterxl/introduction-bde654b5670e (don't worry so much about all the specific libraries, but _do_ think about they do) 48 | 49 | #### JavaScript 50 | - https://javascript.info/ 51 | 52 | 53 | ## Handing it in 54 | The only part of this assignment that must be handed in is the writing assignment. Again, about 500 words should be enough. For this assignment, you can either (1) create a git repository and check the writing in as a .txt or a .md file, or you can (2) email me the assignment at st2774@nyu.edu. 55 | -------------------------------------------------------------------------------- /homework/01-node-hw.md: -------------------------------------------------------------------------------- 1 | # Homework 01 2 | 3 | ## Due Date 4 | 5 | This assignment follows the second lesson, [01-node](../lessons/01-node/01-node.md). 6 | 7 | ## Reading 8 | 9 | - Baker, Kevin T. _Model Metropolis_ https://logicmag.io/play/model-metropolis/ 10 | 11 | ## Assignment - Text Adventure 12 | 13 | Check out this site 14 | http://silly-suggestion.herokuapp.com/ 15 | 16 | This is a text adventure realized entirely as a Node Express app. Your assignment is to make your own text adventure, realized in the same way. 17 | 18 | ### Description 19 | 20 | A text adventure is a kind of video game or interactive novel. Typically (though not always) the player navigates through a dungeon, exploring a series of rooms. The player can perform various actions in each room. The simplest text adventure lets the player choose from a list of options. More advanced games might take in a string of text, and then parse the text in some way to determine what happens next. Complex games might record other statistics for a player, like their standing with various factions, their age, health, height, and so on. These and other factors might determine what options are available to the player at any given point. 21 | 22 | ### Requirements 23 | Overall, I encourage you to push your skills. The base requirements for this assignment are small, but if Express and Node are very familiar to you, then try to add something that flexes a new skill you haven't built up yet. There are some hard requirements for this text adventure though: 24 | 25 | - All of the state of the game must be stored on the server. That means no javascript on the client side. 26 | - The server must be implemented using Express, similar to what we saw in class. 27 | - You're not required to use .html template files, but it's strongly encouraged. 28 | - Don't forget to .gitignore your `node_modules` folder. 29 | - Don't forget to include a README.md. 30 | - The page must be styled using a .css file. That file can be very simple, but it must exist. You can use just one .css file for the whole game. 31 | - The player must be able to collect items and place those items in an inventory. It must be possible to check the inventory at any time (unless maybe the player has gone blind, or is injured, or for some lore-related reason cannot check their inventory). 32 | - The game must have at least one "bad" ending and one "good" ending. The precise meaning of "bad" and "good" are up to you, though in one example, falling into a pit could be the "bad" ending, while escaping the dungeon is a "good" ending. 33 | - A fantasy theme is by no means required. The player doesn't have to navigate a literal labyrinth either. Figuring out which classes to take, for example, is its own kind of labyrinth, and would be an excellent theme for an adventure game. 34 | 35 | In addition, you must take on at least one of these optional requirements. If you're feeling punchy, try and push yourself to take on more than one. 36 | - Using CSS classes, change the appearance of the page depending on the state of the game. For example, if the player is in a garden, the background could be green. 37 | - Implementing an online game with state stored on the server has an issue: there's only one "inventory" that all players share. Use the Express cookie-parser middleware, and use this create several "inventories", one for each player. 38 | - Games are more replayable if the experience varies from one play-through to the next. Add some random elements to your game, so that the exact gameplay experience is different each time you play. 39 | - In class we saw how to use forms to submit text to the server. Using a form, accept text or some other input from the player. Maybe they can set their name, or they have to pronounce a magic spell, or give a password. 40 | - Add multimedia elements to your page. This could include background music, images, or maybe even something fancier. (Don't go too crazy with animations, this is about server-side programming after all.) 41 | - What happens if you have a very clever user, who tries to access a page by typing directly in the address bar? Suppose they type the name of a room that doesn't exist? Can your server handle that? What does its 404 page look like? 42 | 43 | ### Grading 44 | This largely a creative assignment, but creativity is not part of your grade (at least for this assigment). I'm grading this assignment entirely on completeness, so provided (1) your game works and (2) you meet all the requirements, you should be in perfect shape. 45 | 46 | ## Handing it in 47 | Please send me a link to a github repository where your text adventure can be found. I should be able to pull your repository, run `npm install`, and then start up a server and run your text adventure. Please don't forget to create a README.md, even if it only contains a single sentence. If there's anything special I should know about running your game, please add it to the README. 48 | -------------------------------------------------------------------------------- /homework/02-heroku-hw.md: -------------------------------------------------------------------------------- 1 | # Homework 2 - React + Heroku 2 | 3 | ## Reading 4 | 5 | Brain, Tega. _Hack the Planet: Tega Brain on Leaks, Glitches, and Preposterous Futures_ Logic Magazine. Retrieved https://logicmag.io/nature/hack-the-planet-tega-brain-on-leaks-glitches-and-preposterous-futures/ 6 | 7 | ## Tic Tac Toe 8 | 9 | Start by forking the starter template I made for you at https://github.com/starakaj/react-express-starter. Next, implement tic-tac-toe. 10 | 11 | ``` 12 | a b c 13 | | | 14 | 1 - | - | - 15 | _____|_____|_____ 16 | | | 17 | 2 - | - | - 18 | _____|_____|_____ 19 | | | 20 | 3 - | - | - 21 | | | 22 | ``` 23 | 24 | In case you need a reminder, the way tic tac toe works, two players take turns putting an X or an O in each square. If either player gets three in a row, including diagonals, that player wins. 25 | 26 | ### Requirements 27 | - The game must display which player, X or O, has the current turn. 28 | - There must be an indication when one player or the other wins. 29 | - Use a different React component for the game container and for the squares. 30 | - Put it up on Heroku 31 | - Choose at least one of the optional requirements. 32 | 33 | ### Optional 34 | - There must be a way to reset the game (refreshing the page doesn't count). 35 | - Let the players set their names at the start of the game. 36 | - Color the squares differently for X and O using CSS classes. 37 | - Make it possible to undo a move. 38 | - Set the number of rows or columns at the start of the game. 39 | 40 | ### Considerations 41 | - The trickiest part of this assignment is handling clicks on the squares. The individual squares are dumb—they can't know anything about the state of the game. Rather, each square should have an `onClick` prop, similar to a button. 42 | - You don't need to pass a different function down to each of the boxes. Rather, each box can call its `onClick` function with some information (for example the row and column) that identifies that box. 43 | - If you chose to let the players set their names, you probably don't want to use a `post` Express handler. Remember, React has the state, not Express. 44 | 45 | ### Handing it in 46 | 47 | Please email me a github repo, along with a link to your deployed Heroku app. 48 | -------------------------------------------------------------------------------- /homework/03-mongo-hw.md: -------------------------------------------------------------------------------- 1 | # Homework 3 - React + Heroku + Mongo 2 | 3 | ## Reading 4 | 5 | Let me think about it, I'll get it to you soon 6 | 7 | ## Dumb Twitter with filters 8 | 9 | In class we made Dumb Twitter (If you weren't in class, be sure to follow the notes so that you too can build something like dumb-twitter.herokuapp.com). It's like Twitter, but dumb. It's especially dumb in that there's only one big, fat feed. There's no way to look just at tweets made by a particular person, for example. There's also no way to delete a tweet, which as we all know is _critically_ important. 10 | 11 | ### Requirements 12 | - Take the version of Dumb Twitter that we made in class. Make it possible to view only the tweets from a particular person. 13 | - This will require adding a server endpoint that lets you filter over your list of tweets. 14 | - You might find Express request params to be very helpful here: 15 | 16 | ```js 17 | // See https://expressjs.com/en/api.html#path-examples 18 | app.get('/user/:id', function (req, res) { 19 | res.send('user ' + req.params.id); 20 | }); 21 | ``` 22 | 23 | - Make it possible to delete a Tweet. You might consider using the DELETE HTTP verb, in conjunction with https://expressjs.com/en/api.html#app.delete.method 24 | - Remember, you'll have to set `method: "DELETE"` in your fetch. 25 | - Use MongoDB to store your data 26 | - Put it up on Heroku 27 | - You must complete NONE of the optional parts. But if you're feeling eager, you should go for it. 28 | 29 | ### Optional 30 | - Add test search to your Dumb Twitter. Make it possible to search for hashtags 31 | - Rather than forcing people to type their name with every tweet, have them log in to an account 32 | - Add a date to each tweet as it's created, making it possible to search over tweets by date 33 | - Style your page a bit so it doesn't look crappy 34 | 35 | ### Handing it in 36 | 37 | Please email me a github repo, along with a link to your deployed Heroku app. 38 | -------------------------------------------------------------------------------- /homework/04-raspberry-hw.md: -------------------------------------------------------------------------------- 1 | # Homework 4 - Raspberry Weather Station 2 | 3 | ## Reading 4 | 5 | Look in 05-midterm-hw 6 | 7 | ## Weather Station 8 | 9 | Compared to some of the other homework that you've had previously, this one is going to seem very simple. All you need to do is modify the existing sensor reading code to record temperature and humidity readings. Then you need to write a quick script to scan a log file and report min + max values. 10 | 11 | ### Requirements 12 | - Modify the existing code on the Raspberry Pi so that it records temperature to a log. 13 | - Log that high and low somewhere using `bunyan`. 14 | - Write a Node script that will take a calendar date and a log file as input, returning the temperature high and low for that day. 15 | - You might need to read "https://nodejs.org/en/knowledge/command-line/how-to-parse-command-line-arguments/" for help on processing command line input. 16 | 17 | ### Handing it in 18 | - Please email me a github repo containing the source code. Please include with it a sample log file recorded from the Raspberry Pi. 19 | - You might need to use `scp` to copy a file from your Raspberry Pi to your desktop. 20 | -------------------------------------------------------------------------------- /homework/05-midterm-hw.md: -------------------------------------------------------------------------------- 1 | # Midterm Preparation 2 | 3 | ## Reading 4 | We're preparing for a group project next week, so the reading should get you in the mindset of group software work. 5 | 6 | - "The Scrum Guide" which you can download here https://www.scrumguides.org/index.html. 7 | - "Why 'Agile' and especially Scrum are terrible" https://michaelochurch.wordpress.com/2015/06/06/why-agile-and-especially-scrum-are-terrible/ 8 | - Watch "Connected Worlds" https://nysci.org/home/exhibits/connected-worlds/ 9 | 10 | ## Miderm Assignment 11 | 12 | This isn't a full description of the assignment, but rather it's just a bit of prep work to get you ready for the assignment. We're going to be using the midterm to put together all the knowledge that we've accumulated so far. The project will: 13 | 14 | - Take readings from a Raspberry Pi. 15 | - Push that data server and store it in MongoDB. 16 | - Present that data through a client-facing API. 17 | - Display the data in a client-side, React powered web app. 18 | 19 | It's a bit of a funny way to work, in that I'm telling you what pieces you have to use but not what you should use them for. But this basic backbone is extremely flexible and can drive a huge number of different projects. So prepare for the next class by answering the following questions: 20 | 21 | - What kind of story can you tell with data gathered from a Raspberry Pi? 22 | - What kind of sensor data do you want to work with? 23 | - How can you analyze that data once its in a database? 24 | - What might someone on the client side want to do with that data? 25 | - How does that data become meaningful for someone? 26 | -------------------------------------------------------------------------------- /homework/07-audio-hw.md: -------------------------------------------------------------------------------- 1 | # Homework 07 2 | 3 | ## Due Date 4 | 5 | This assignment follows the audio analysis lesson, [07-audio](../lessons/07-audio/07-audio.md). It must be turned in by midnight, March 31. 6 | 7 | ## Reading 8 | 9 | Optionally, watch this very fun interview with Tarik Barri - https://www.youtube.com/watch?v=xtKnF_9XM_o 10 | 11 | ## Assignment - Audio Visualizer 12 | 13 | Take a look at this 14 | https://willianjusten.com.br/audio-experiments/triangles/ 15 | 16 | This is a very simple, yet effective audio visualizer. A number of triangles rotates around a center point. The size of each triangle is proportional to the energy in a particular part of the audio spectrum. You could use the "loudness" feature of the Meyda analyzer, which contains a property called "specific". Each element of this array contains the apparent loudness of a different portion of the audio spectrum. So, you could use this feature to scale the radius of each triangle. 17 | 18 | ### Description 19 | 20 | An audio visualizer creates an evolving and dynamic visual scene, parametrized in some way to reflect the salient features of an audio source. In the case above, the radius of each triangle is proportional to the energy in the spectrum. A visualizer could also draw on the chroma, or the spectral centroid, or any number of other features. For this assignment, make an audio visualizer, using either a microphone or sound file as an audio source. 21 | 22 | ### Requirements 23 | One word of advice: try not to spend too much time on getting a specific piece of data out of the audio. There are problems like accurately detecting beat, or determining the exact point in time at which a new note begins, that can be very difficult and time consuming. Try to work with the data that you have available. 24 | 25 | Requirements: 26 | - You may use any graphics library that you like. If you're familiar with WebGL or another drawing technology, then feel free to use that instead of p5 in instance mode. However, you should write your own visualizer code. It's fine to be inspired by something that you find on shadertoy, but it's not acceptible to copy something that you found online for the visual portion of the assignment. 27 | - You must use at least two of the analysis parameters exposed by Meyda.js. 28 | 29 | You must also complete one of these optional requirements: 30 | - Control the parameters of your audio visualization with dat.gui 31 | - Add a way to switch between different audio tracks. 32 | - Add a way to switch between different video "scenes." For example, you might have a p5 draw function that works in a completely different way, depending on the value of a toggle. 33 | 34 | ### Grading 35 | Again, this is a creative assignment, but creativity is not part of the grade. I'm excited to see what you come up with, but it's most important to me that you complete the requirements. If it's clear that you spent a bit of time with the assignment, and that you worked with Meyda enough to come up with a visualization that you're happy with, then you have nothing to worry about. 36 | 37 | ## Handing it in 38 | Please send me a link to a github repository where your visualizer can be found. I should be able to pull your repository, run `npm install`, and then start up a server and run your visualizer. Please don't forget to create a README.md, even if it only contains a single sentence. If there's anything special I should know about running your visualizer, please add it to the README. The README is also a great place to talk about any problems that you ran into, or to highlight any particular things that you're especially proud of. If your visualizer works best with a particular piece of audio, please include a link where I can find it, but don't add the audio file to your repository. 39 | -------------------------------------------------------------------------------- /homework/08-video-hw.md: -------------------------------------------------------------------------------- 1 | # Homework 08 2 | 3 | ## Due Date 4 | 5 | This assignment follows the video analysis lesson, [08-video](../lessons/08-video/08-video.md). It must be turned in by midnight, April 7. 6 | 7 | ## Reading 8 | 9 | None, just relax. 10 | 11 | ## Assignment - Video into sound, or video into drawing 12 | In class we looked at two example of how to process video, one generating sound and the other creating a visualization. For this assignment, develop one of those processes further. If you've worked with Tone before, or you're more interested in sound, you can pursue that path. Otherwise you can do more drawing based on video data. 13 | 14 | ### Description 15 | If you choose to do some drawing based on video analysis, you might start from the optical flow example that we looked at in class. You're also free to take another one of the Computer Vision examples from Kyle's page and to start from there. If you'd rather work from face data instead, start from the face tracking example. Rather than sending out particles from the points of optical flow, do something else. Things you might experiment with: 16 | 17 | #### Video 18 | - Drawing text based on movement 19 | - Distorting a shape using video movement 20 | - Directing a swarm of particles using the edges in the video 21 | - Giving the video an animated color depending on movement 22 | - Making an "instagram filter" by drawing shapes or images on top of the face 23 | 24 | #### Audio 25 | - Add an audio effect to the processing chain, like reverb, filter or distortion. Control this with the face or with movement. 26 | - Control the playback of an audio file using the video, perhaps adjusting its speed using the position of your face. 27 | - Play a chord of multiple different notes. Maybe one note is controlled by each eye, and a third with the mouth. 28 | 29 | ### Requirements 30 | There are no hard requirements for this assignment, except that it must use Computer Vision in some way. So, it could be based on one of the CV examples from Kyle's page, or it could use the face tracking data from the ml5 example. You should also try to go beyond simply tweaking what we did in class. 31 | 32 | ### Grading 33 | Again, this is a creative assignment, but creativity is not part of the grade. I'm excited to see what you come up with, but it's most important to me that you complete the requirements. If it's clear that you spent a bit of time with the assignment, and you experimented with something new, then you have nothing to worry about. 34 | 35 | ## Handing it in 36 | Please send me a link to a github repository where your project can be found. I should be able to pull your repository, run `npm install`, and then start up a server and run the example. Please don't forget to create a README.md, even if it only contains a single sentence. The README is also a great place to talk about any problems that you ran into, or to highlight any particular things that you're especially proud of. If you're doing some kind of complex audio mapping, please describe what you were trying to achieve in the README. 37 | -------------------------------------------------------------------------------- /homework/09-language-hw.md: -------------------------------------------------------------------------------- 1 | # Homework 09 2 | 3 | ## Due Date 4 | 5 | This assignment follows the natural language processing lesson, [09-language](../lessons/09-language/09-language.md). It must be turned in by midnight, April 14. 6 | 7 | ## Assignment - Automatically generated poem 8 | In class we looked at how to draw text from a website, and how to rearrange that text into something like a poem. We looked at how to count syllables, how to register parts of speech, and how to figure out (kind of) when words rhymed. For homework I want you to build on what we talked about in class. Using the poem starter at https://github.com/starakaj/poem-starter, modify the contents of `poem/poem.js` to read in a page from the Internet, do something with its contents, and then return some text to the user that has a poetic form. 9 | 10 | ### Description 11 | After copying the starter, open up the file at `poem/poem.js`. Using the techniques we talked about in class, pull down a webpage and try to do something interesting with its contents. The file should export a `makePoem` function, so that the server (at `server.js` ) can return a poem at the `/poem` endpoint. You have many options open to you, the only requirement is that the function must return an array of text, each element of which is one line of something poetic. 12 | 13 | ### Requirements 14 | When I say that the text returned by `makePoem` must be poetic, it's not entirely clear what that means. A poem is simply a creative arrangement of text, so that certainly leaves you a lot of leeway. Some things that you might try include: 15 | 16 | - Pulling blocks of text containing a common word (look into stemming!) 17 | - Listing only words that have a "negative" or "positive" sentiment (you can use sentiment analysis for this) 18 | - Writing your own new sentences, using vocabulary drawn from the text 19 | - Writing lines with the same number of syllables 20 | - Writing a limerick 21 | - Cutting sentences in half, and then mixing them up and sticking them back together 22 | 23 | If you want, you're strongly encouraged to make the result into a single-page web application. You can use a React form (see [the lesson where we made dumb twitter](../lessons/03-persistence/03-persistence.md) for a refresher) to submit a page to the server, which then returns a poem for rendering. If you like, you can get creative with how the text is displayed. You might try: 24 | 25 | - Size words larger or smaller depending on some criteria 26 | - Arrange words to draw a picture 27 | - Analyze links from the starting page, and use this to draw a map of how pages are connected 28 | 29 | ### Grading 30 | Like other recent assignments, this is a creative assignment, but creativity is not part of the grade. I'm excited to see what you come up with, but it's most important to me that you complete the requirements. If it's clear that you spent a bit of time with the assignment, and you experimented with something new, then you have nothing to worry about. 31 | 32 | ## Handing it in 33 | Please send me a link to a github repository where your project can be found. I should be able to pull your repository, run `npm install`, and then run `npm run poem` to generate a poem. If you had the time, I should also be able to run `npm run watch` to see the poem in the browser. Please don't forget to create a README.md, even if it only contains a single sentence. The README is also a great place to talk about any problems that you ran into, or to highlight any particular things that you're especially proud of. If you're doing some kind of complex audio mapping, please describe what you were trying to achieve in the README. 34 | -------------------------------------------------------------------------------- /homework/10-learning-hw.md: -------------------------------------------------------------------------------- 1 | # Homework 10 2 | 3 | ## Due Date 4 | 5 | This assignment follows the Teachable Machine lesson, [10-learning](../lessons/10-learning/10-learning.md). It must be turned in by midnight, April 21. 6 | 7 | ## Assignment - Teachable interface 8 | Go ahead into `lessons/10-learning/teachable-machine-app` and run `npm install`. Then go into `app.js` and make sure that `heartHands/heartHandsApp` is the source of the `setup` function. Now `npm run watch` and navigate to `localhost:3000`. Hopefully, when you make a heart with your hands, you'll see a flurry of pink hearts. What I'd like you to do in this assignment is to think about how Teachable Machine can be used as an interface. 9 | 10 | ### Description 11 | Simply put, make something with Teachable Machine. Check out this https://experiments.withgoogle.com/interplay-mode/view/ 12 | 13 | Here, we basically have something not significantly more complicated than what we already made in class. The UI is a lot prettier, and it's playing with a nice video, but we already saw how to make something like this just the other day. What else can you imagine that might make use of some of what we saw how to do in class? 14 | 15 | - A drawing canvas, where the things that you draw emerge from the page when they are recognized 16 | - An adventure game, where you make hand signs to determine what action you take 17 | - Something like Berghain Trainer https://berghaintrainer.com/ 18 | - A "conversation" with an animated character that can recognize certain words 19 | - An instagram filter (would combine nicely with the head tracker from two lessons ago) 20 | - A sonifier that analyzes your face and tries to play music that matches your mood 21 | 22 | ### Requirements 23 | You must use an original model, not one of the ones that we used in class. You may either upload the model to Google, or include it as part of your repo. You don't have to use p5, you don't even have to do any drawing at all. The important part is that you use an original model in your own code. Other than that, you have a lot of freedom on this assignment. 24 | 25 | ### Grading 26 | Like other recent assignments, this is a creative assignment, but creativity is not part of the grade. I'm excited to see what you come up with, but it's most important to me that you complete the requirements. If it's clear that you spent a bit of time with the assignment, and you experimented with something new, then you have nothing to worry about. 27 | 28 | ## Handing it in 29 | Please send me a link to a github repository where your project can be found. I should be able to pull your repository, run `npm install`, and then run `npm run watch` or `npm run start` to see your code in action. Please don't forget to create a README.md, even if it only contains a single sentence. The README is also a great place to talk about any problems that you ran into, or to highlight any particular things that you're especially proud of. If you're doing some kind of complex audio mapping, please describe what you were trying to achieve in the README. 30 | -------------------------------------------------------------------------------- /lessons/00-intro/img/LICENSE.md: -------------------------------------------------------------------------------- 1 | #### container.jpg 2 | By Mr Snrub at the English language Wikipedia, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=7005472 3 | -------------------------------------------------------------------------------- /lessons/00-intro/img/atm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/00-intro/img/atm.jpg -------------------------------------------------------------------------------- /lessons/00-intro/img/bank-teller.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/00-intro/img/bank-teller.jpg -------------------------------------------------------------------------------- /lessons/00-intro/img/container.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/00-intro/img/container.jpg -------------------------------------------------------------------------------- /lessons/00-intro/img/harmonic_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/00-intro/img/harmonic_table.png -------------------------------------------------------------------------------- /lessons/00-intro/img/notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/00-intro/img/notes.png -------------------------------------------------------------------------------- /lessons/00-intro/img/spectrogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/00-intro/img/spectrogram.png -------------------------------------------------------------------------------- /lessons/00-intro/img/waveform_visual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/00-intro/img/waveform_visual.png -------------------------------------------------------------------------------- /lessons/01-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01-node", 3 | "version": "1.0.0", 4 | "description": "Lesson 01: An introduction to Node", 5 | "main": "src/server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "serve": "node src/server.js", 9 | "info": "node src/os-info.js" 10 | }, 11 | "author": "Sam Tarakajian", 12 | "license": " CC-BY-SA-4.0", 13 | "dependencies": { 14 | "chance": "^1.1.4", 15 | "cookie-parser": "^1.4.4", 16 | "express": "^4.17.1", 17 | "express-useragent": "^1.0.13" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lessons/01-node/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: aqua; 3 | } -------------------------------------------------------------------------------- /lessons/01-node/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { randomName, randomPlace } = require("./src/namer"); 3 | const port = process.env.PORT || 3000; 4 | const fs = require("fs"); 5 | 6 | // Create an instance of the express application 7 | const app = express(); 8 | 9 | // Serve the public directory as static 10 | app.use(express.static("public")); 11 | 12 | let counter = 0; 13 | let name = null; 14 | 15 | app.get("/", (req, res) => { 16 | counter++; 17 | let htmldoc = fs.readFileSync("./templates/index.html", "utf8"); 18 | htmldoc = htmldoc.replace("%%%VIEWS%%%", counter); 19 | res.send( 20 | htmldoc 21 | ); 22 | }); 23 | 24 | // You can use replace to pretend like you're using React 25 | app.get("/identity", (req, res) => { 26 | const newName = randomName(); 27 | const newAddress = randomPlace(); 28 | let htmldoc = fs.readFileSync("./templates/index.html", "utf8"); 29 | htmldoc = htmldoc.replace("%%%NAME%%%", newName); 30 | htmldoc = htmldoc.replace("%%%PLACE%%%", newAddress); 31 | res.send( 32 | htmldoc 33 | ); 34 | }); 35 | 36 | 37 | app.listen(port, () => { 38 | console.log(`Express is listening on port ${port}`); 39 | }); 40 | -------------------------------------------------------------------------------- /lessons/01-node/src/namer.js: -------------------------------------------------------------------------------- 1 | const chance = require("chance"); // load the chance module 2 | const c = new chance(); // Create an actual chance instace. See the docs. 3 | 4 | // Only run these lines if you're running the file as a script 5 | if (require.main === module) { 6 | console.log(`Your new random name is ${c.name()}`); 7 | console.log(`You live on ${c.street()} in ${c.state()}`); 8 | } 9 | 10 | module.exports = { 11 | randomName: () => c.name(), 12 | randomPlace: () => `${c.street()}, ${c.state()}` 13 | } 14 | -------------------------------------------------------------------------------- /lessons/01-node/src/os-info.js: -------------------------------------------------------------------------------- 1 | const os = require("os"); 2 | 3 | console.log(`Running on platform ${os.platform()}`); 4 | console.log(`Running on architecture ${os.arch()}`); 5 | -------------------------------------------------------------------------------- /lessons/01-node/templates/changename.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Change your name 4 | 5 | 6 | 7 |

Change your name

8 |

%%%CURRENT_NAME%%%

9 |
10 | 11 | 12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /lessons/01-node/templates/identity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Your new identity 4 | 5 | 6 | 7 |

Your name is %%%NAME%%%, you live at %%%PLACE%%%

8 | 9 | 10 | -------------------------------------------------------------------------------- /lessons/01-node/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Homepage 4 | 5 | 6 | 7 |

Cool Node Server

8 |

This page has been viewed %%%VIEWS%%% times

9 |

Try out these fun paths:

10 | 14 | 15 | -------------------------------------------------------------------------------- /lessons/01-node/templates/nameset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Name set 4 | 5 | 6 | 7 |

Name set successful

8 |

%%%CURRENT_NAME%%%

9 | Return to homepage 10 | 11 | -------------------------------------------------------------------------------- /lessons/02-heroku/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", "@babel/preset-react"] 3 | } -------------------------------------------------------------------------------- /lessons/02-heroku/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /lessons/02-heroku/img/01-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/02-heroku/img/01-template.png -------------------------------------------------------------------------------- /lessons/02-heroku/img/02-clone-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/02-heroku/img/02-clone-mode.png -------------------------------------------------------------------------------- /lessons/02-heroku/img/03-hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/02-heroku/img/03-hello.png -------------------------------------------------------------------------------- /lessons/02-heroku/img/04-oopsie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/02-heroku/img/04-oopsie.png -------------------------------------------------------------------------------- /lessons/02-heroku/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "02-heroku", 3 | "version": "1.0.0", 4 | "description": "Lesson 02: Server-side rendering with React, and deploying the whole stupid thing to Heroku", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "babel src --out-dir dist --source-maps inline", 8 | "serve": "npm run build && node server/index.js --PORT=3000", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "Sam Tarakajian", 12 | "license": " CC-BY-SA-4.0", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.0", 15 | "@babel/core": "^7.1.0", 16 | "@babel/preset-env": "^7.1.0", 17 | "@babel/preset-react": "^7.0.0" 18 | }, 19 | "dependencies": { 20 | "express": "^4.17.1", 21 | "react": "^16.5.2", 22 | "react-dom": "^16.5.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lessons/02-heroku/server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { fullAppMarkdown } = require("../dist/App"); 3 | const port = process.env.PORT || 3000; 4 | 5 | const app = express(); 6 | 7 | app.get("/", (req, res) => { 8 | const fullMarkdown = fullAppMarkdown(); 9 | res.send(fullMarkdown); 10 | }); 11 | 12 | app.listen(port, () => { 13 | console.log(`Express app listening on port ${port}`); 14 | }); 15 | -------------------------------------------------------------------------------- /lessons/02-heroku/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { Welcome } from "./Welcome"; 4 | import { ServerInfo } from "./ServerInfo"; 5 | import os from "os"; 6 | 7 | export function fullAppMarkdown() { 8 | return ReactDOMServer.renderToStaticMarkup( 9 | ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /lessons/02-heroku/src/ServerInfo.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const ServerInfo = (props) => { 4 | const { 5 | platform, 6 | arch 7 | } = props; 8 | return
9 | This page is server from a {platform} machine on a {arch} architecture 10 |
11 | } -------------------------------------------------------------------------------- /lessons/02-heroku/src/Welcome.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Welcome = () => { 4 | return

Welcome to this highly cool and interesting page

5 | } 6 | -------------------------------------------------------------------------------- /lessons/03-persistence/Procfile: -------------------------------------------------------------------------------- 1 | web: yarn install && node lessons/03-persistence/src/mongo/app.js 2 | -------------------------------------------------------------------------------- /lessons/03-persistence/img/mlab-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/03-persistence/img/mlab-01.png -------------------------------------------------------------------------------- /lessons/03-persistence/img/mlab-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/03-persistence/img/mlab-02.png -------------------------------------------------------------------------------- /lessons/03-persistence/img/postman-json-body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/03-persistence/img/postman-json-body.png -------------------------------------------------------------------------------- /lessons/03-persistence/img/postman-json-headers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/03-persistence/img/postman-json-headers.png -------------------------------------------------------------------------------- /lessons/03-persistence/img/smocking.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/03-persistence/img/smocking.jpg -------------------------------------------------------------------------------- /lessons/03-persistence/img/webapp-diagram.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/03-persistence/img/webapp-diagram.ai -------------------------------------------------------------------------------- /lessons/03-persistence/img/webapp-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/03-persistence/img/webapp-diagram.png -------------------------------------------------------------------------------- /lessons/03-persistence/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "03-persistence", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/mongo/app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Sam Tarakajian", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.1", 13 | "mongodb": "^3.4.1" 14 | }, 15 | "engines": { 16 | "node": "12.x" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lessons/03-persistence/src/json/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const port = process.env.PORT || 3000; 3 | const { promisify } = require("util"); 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | const access = promisify(fs.access); 7 | 8 | const app = express(); 9 | 10 | app.get("/", async (req, res) => { 11 | 12 | const datafile = path.join(__dirname, "data.json"); 13 | 14 | try { 15 | let data = { 16 | pageviews: 0 17 | }; 18 | 19 | try { 20 | await access(datafile, fs.constants.F_OK); 21 | const filecontents = fs.readFileSync(datafile, { encoding: "utf8" }); 22 | data = JSON.parse(filecontents); 23 | } catch { /* We don't acually care if there's an error here */ } 24 | 25 | // Increment the number of pageviews 26 | data.pageviews = data.pageviews + 1; 27 | 28 | // Write the result back to disk 29 | fs.writeFileSync(datafile, JSON.stringify(data)); 30 | 31 | // Finally, report back the number of pageviews 32 | res.send(`This page has been viewed ${data.pageviews} times`); 33 | 34 | } catch (e) { 35 | res.send("Some kind of terrible error happened"); 36 | 37 | console.dir(e); 38 | } 39 | }); 40 | 41 | app.listen(port, () => { 42 | console.log(`Express app listening on port ${port}`); 43 | }); 44 | -------------------------------------------------------------------------------- /lessons/03-persistence/src/json/data.json: -------------------------------------------------------------------------------- 1 | {"pageviews":4} -------------------------------------------------------------------------------- /lessons/03-persistence/src/mongo-basic/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const port = process.env.PORT || 3000; 3 | 4 | // Require mongo. Here we're pulling MongoClient out of the require and immediately storing 5 | // it in a variable called mongo. 6 | const mongo = require('mongodb').MongoClient; 7 | 8 | // This is the URL for connecting to the database. As you can see, we're allowing this 9 | // to be set from the outside, or setting it ourselves. We've used this strategy before 10 | // with process.env.PORT for Heroku, and we're doing the exact same thing here. 11 | let url = process.env.MONGODB_URI || "mongodb://localhost:27017/my-database"; 12 | 13 | const app = express(); 14 | 15 | // This will store our database connection 16 | let dbClient; 17 | 18 | // Very important that we pass an asynchronous function here to app.get, so that we can 19 | // use await inside it. 20 | app.get("/", async (req, res) => { 21 | 22 | try { 23 | // Get a collection, which is like a single bundle of stuff within our database 24 | const collection = await dbClient.collection("pagemeta"); 25 | 26 | // The pagemeta collection holds only one item, so we can call findOne, passing 27 | // in empty search criteria, to just get that one thing 28 | const item = await collection.findOne({}); 29 | 30 | // Increment the number of pageviews, if we've seen the page before 31 | let pageviews = 1; 32 | if (item) pageviews = item.pageviews + 1; 33 | 34 | // Update the item in the database, inserting if it's not there. That's what 35 | // upsert means. 36 | await collection.updateOne({}, { 37 | $set: { 38 | pageviews: pageviews 39 | } 40 | }, { 41 | upsert: true 42 | }); 43 | 44 | // Finally, report back the number of pageviews 45 | res.send(`This page has been viewed ${pageviews} times`); 46 | 47 | } catch (e) { 48 | res.status(500).send("Some kind of terrible error happened"); 49 | console.log(e); 50 | } 51 | }); 52 | 53 | // We're not using async/await here because there's no top-level await 54 | // First connect to the database given the url. 55 | mongo.connect(url, { 56 | useNewUrlParser: true, 57 | useUnifiedTopology: true 58 | }).then((client) => { 59 | 60 | // Store the client connection so that we can use it later 61 | dbClient = client.db(); 62 | 63 | // Finally, start the server like normal 64 | app.listen(port, () => { 65 | console.log(`Express app listening on port ${port}`); 66 | }); 67 | }).catch((err) => { 68 | console.log("Couldn't connect to the database"); 69 | console.log(err); 70 | }); 71 | -------------------------------------------------------------------------------- /lessons/03-persistence/src/trivial/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | 3 | const app = express(); 4 | const port = process.env.PORT || 3000; 5 | 6 | let count = 0; 7 | 8 | app.get("/", (_, res) => { 9 | res.send(` 10 | 11 | 12 | This page has been visited ${++count} times 13 | 14 | 15 | `); 16 | }); 17 | 18 | app.listen(port, () => { 19 | console.log(`Listening on port ${port}`); 20 | }); 21 | -------------------------------------------------------------------------------- /lessons/04-raspberry/code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /lessons/04-raspberry/code/index.js: -------------------------------------------------------------------------------- 1 | console.log("Starting node sensor script"); 2 | 3 | const sensor = require("node-dht-sensor").promises; 4 | 5 | sensor.setMaxRetries(10); 6 | 7 | setInterval(async () => { 8 | try { 9 | const reading = await sensor.read(22, 4); 10 | const temp = reading.temperature; 11 | const humi = reading.humidity; 12 | console.log(`temp: ${temp}°C, humidity: ${humi}%`); 13 | } catch (e) { 14 | console.log("Error!"); 15 | console.log(e); 16 | } 17 | }, 1000); 18 | -------------------------------------------------------------------------------- /lessons/04-raspberry/code/logging-test.js: -------------------------------------------------------------------------------- 1 | const bunyan = require("bunyan"); 2 | const log = bunyan.createLogger({ 3 | name: "dht", 4 | streams: [ 5 | { 6 | stream: process.stdout, 7 | level: "debug" 8 | }, 9 | { 10 | path: "/var/tmp/dhtsensor.log", 11 | level: "error" 12 | } 13 | ] 14 | }); 15 | 16 | log.info("This will be printed to the console, since 'info' is a log level above 'debug'"); 17 | log.error("This will also be written to a log file, since the file stream is for 'error' and higher"); 18 | -------------------------------------------------------------------------------- /lessons/04-raspberry/code/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raspberry-pi-dht-example", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "balanced-match": { 8 | "version": "1.0.0", 9 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 10 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 11 | "optional": true 12 | }, 13 | "brace-expansion": { 14 | "version": "1.1.11", 15 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 16 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 17 | "optional": true, 18 | "requires": { 19 | "balanced-match": "^1.0.0", 20 | "concat-map": "0.0.1" 21 | } 22 | }, 23 | "bunyan": { 24 | "version": "1.8.12", 25 | "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", 26 | "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", 27 | "requires": { 28 | "dtrace-provider": "~0.8", 29 | "moment": "^2.10.6", 30 | "mv": "~2", 31 | "safe-json-stringify": "~1" 32 | } 33 | }, 34 | "concat-map": { 35 | "version": "0.0.1", 36 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 37 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 38 | "optional": true 39 | }, 40 | "dtrace-provider": { 41 | "version": "0.8.8", 42 | "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", 43 | "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", 44 | "optional": true, 45 | "requires": { 46 | "nan": "^2.14.0" 47 | } 48 | }, 49 | "glob": { 50 | "version": "6.0.4", 51 | "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", 52 | "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", 53 | "optional": true, 54 | "requires": { 55 | "inflight": "^1.0.4", 56 | "inherits": "2", 57 | "minimatch": "2 || 3", 58 | "once": "^1.3.0", 59 | "path-is-absolute": "^1.0.0" 60 | } 61 | }, 62 | "inflight": { 63 | "version": "1.0.6", 64 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 65 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 66 | "optional": true, 67 | "requires": { 68 | "once": "^1.3.0", 69 | "wrappy": "1" 70 | } 71 | }, 72 | "inherits": { 73 | "version": "2.0.4", 74 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 75 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 76 | "optional": true 77 | }, 78 | "minimatch": { 79 | "version": "3.0.4", 80 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 81 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 82 | "optional": true, 83 | "requires": { 84 | "brace-expansion": "^1.1.7" 85 | } 86 | }, 87 | "minimist": { 88 | "version": "0.0.8", 89 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 90 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 91 | "optional": true 92 | }, 93 | "mkdirp": { 94 | "version": "0.5.1", 95 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 96 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 97 | "optional": true, 98 | "requires": { 99 | "minimist": "0.0.8" 100 | } 101 | }, 102 | "moment": { 103 | "version": "2.24.0", 104 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", 105 | "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", 106 | "optional": true 107 | }, 108 | "mv": { 109 | "version": "2.1.1", 110 | "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", 111 | "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", 112 | "optional": true, 113 | "requires": { 114 | "mkdirp": "~0.5.1", 115 | "ncp": "~2.0.0", 116 | "rimraf": "~2.4.0" 117 | } 118 | }, 119 | "nan": { 120 | "version": "2.14.0", 121 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", 122 | "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", 123 | "optional": true 124 | }, 125 | "ncp": { 126 | "version": "2.0.0", 127 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 128 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", 129 | "optional": true 130 | }, 131 | "node-addon-api": { 132 | "version": "1.7.1", 133 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.1.tgz", 134 | "integrity": "sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ==" 135 | }, 136 | "node-dht-sensor": { 137 | "version": "0.4.1", 138 | "resolved": "https://registry.npmjs.org/node-dht-sensor/-/node-dht-sensor-0.4.1.tgz", 139 | "integrity": "sha512-sSKHTBBMWijHih+YVWMYo+XkdJK6UmzjBygWQtMlWqyi0Fj4avRZ3/5mevxuDJSRMJ/AroszuEREe5LDn0SF9A==", 140 | "requires": { 141 | "node-addon-api": "^1.6.3" 142 | } 143 | }, 144 | "once": { 145 | "version": "1.4.0", 146 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 147 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 148 | "optional": true, 149 | "requires": { 150 | "wrappy": "1" 151 | } 152 | }, 153 | "path-is-absolute": { 154 | "version": "1.0.1", 155 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 156 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 157 | "optional": true 158 | }, 159 | "rimraf": { 160 | "version": "2.4.5", 161 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", 162 | "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", 163 | "optional": true, 164 | "requires": { 165 | "glob": "^6.0.1" 166 | } 167 | }, 168 | "safe-json-stringify": { 169 | "version": "1.2.0", 170 | "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", 171 | "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", 172 | "optional": true 173 | }, 174 | "wrappy": { 175 | "version": "1.0.2", 176 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 177 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 178 | "optional": true 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /lessons/04-raspberry/code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raspberry-pi-dht-example", 3 | "version": "1.0.0", 4 | "description": "Example code from Programming is the Art of the Possible for running DTH22 code on a Raspberry Pi", 5 | "main": "index.js", 6 | "scripts": { 7 | "rsync": "rsync -avz -e ssh --exclude=node_modules --delete . pi@raspi-one.local:/home/pi/pip-dht-example", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/starakaj/programming-is-possible.git" 13 | }, 14 | "author": "@starakaj", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/starakaj/programming-is-possible/issues" 18 | }, 19 | "homepage": "https://github.com/starakaj/programming-is-possible#readme", 20 | "dependencies": { 21 | "bunyan": "^1.8.12", 22 | "node-dht-sensor": "^0.4.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lessons/04-raspberry/code/yarn-error.log: -------------------------------------------------------------------------------- 1 | Arguments: 2 | /Users/samueltarakajian/.nvm/versions/node/v12.14.1/bin/node /usr/local/Cellar/yarn/1.21.1/libexec/bin/yarn.js run rsync 3 | 4 | PATH: 5 | /Users/samueltarakajian/.nvm/versions/node/v12.14.1/bin:/Users/samueltarakajian/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/Library/Frameworks/Mono.framework/Versions/Current/Commands 6 | 7 | Yarn version: 8 | 1.21.1 9 | 10 | Node version: 11 | 12.14.1 12 | 13 | Platform: 14 | darwin x64 15 | 16 | Trace: 17 | SyntaxError: /Users/samueltarakajian/nyu/programming-is-possible/lessons/05-raspberry/code/package.json: Unexpected string in JSON at position 332 18 | at JSON.parse () 19 | at /usr/local/Cellar/yarn/1.21.1/libexec/lib/cli.js:1625:59 20 | at Generator.next () 21 | at step (/usr/local/Cellar/yarn/1.21.1/libexec/lib/cli.js:310:30) 22 | at /usr/local/Cellar/yarn/1.21.1/libexec/lib/cli.js:321:13 23 | 24 | npm manifest: 25 | { 26 | "name": "raspberry-pi-dht-example", 27 | "version": "1.0.0", 28 | "description": "Example code from Programming is the Art of the Possible for running DTH22 code on a Raspberry Pi", 29 | "main": "index.js", 30 | "scripts": { 31 | "rsync": "rsync -avz -e ssh --exclude=node_modules --delete . pi@raspi-one.local:/home/pi/pip-dht-example" 32 | "test": "echo \"Error: no test specified\" && exit 1" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/starakaj/programming-is-possible.git" 37 | }, 38 | "author": "@starakaj", 39 | "license": "ISC", 40 | "bugs": { 41 | "url": "https://github.com/starakaj/programming-is-possible/issues" 42 | }, 43 | "homepage": "https://github.com/starakaj/programming-is-possible#readme", 44 | "dependencies": { 45 | "node-dht-sensor": "^0.4.1" 46 | } 47 | } 48 | 49 | yarn manifest: 50 | No manifest 51 | 52 | Lockfile: 53 | No lockfile 54 | -------------------------------------------------------------------------------- /lessons/04-raspberry/hw/get_temp_range.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { promisify } = require("util"); 3 | const moment = require("moment"); 4 | const path = require("path"); 5 | const readFileAsync = promisify(fs.readFile); 6 | 7 | // Get command line arguments 8 | const myArgs = process.argv.slice(2); 9 | if (myArgs.length < 2) { 10 | console.log("Usage: node get_temp_range.js "); 11 | process.exit(0); 12 | } 13 | 14 | const filename = myArgs[0]; 15 | const filterdate = myArgs[1]; 16 | 17 | readFileAsync(filename, "utf8").then((contents) => { 18 | // Split on newlines and filter out empty lines 19 | const lines = contents.split("\n").filter(line => line.length > 0); 20 | // Parse each line as JSON 21 | const parsedLines = lines.map(JSON.parse); 22 | // Filter for those lines with the same day, using the granularity controls from moment 23 | const filteredLines = parsedLines.filter(line => { 24 | return moment(line.time).isSame(filterdate, "day"); 25 | }); 26 | const maxTemp = filteredLines.map(line => line.temp).reduce((a, b) => Math.max(a, b)); 27 | const minTemp = filteredLines.map(line => line.temp).reduce((a, b) => Math.min(a, b)); 28 | console.log(`Temperature min: ${minTemp} max: ${maxTemp}`); 29 | }).catch(e => console.error(e)); 30 | -------------------------------------------------------------------------------- /lessons/04-raspberry/hw/make_test_data.js: -------------------------------------------------------------------------------- 1 | const bunyan = require("bunyan"); 2 | const path = require("path"); 3 | const moment = require("moment"); 4 | const log = bunyan.createLogger({ 5 | name: 'test', 6 | streams: [ 7 | { 8 | level: 'info', 9 | path: path.join(__dirname, "test.log") 10 | } 11 | ] 12 | }); 13 | 14 | // Make data for the last 7 days 15 | for (let d = 0; d < 7; d++) { 16 | 17 | // Make data for 24 hours in that day 18 | for (let h = 0; h < 24; h++) { 19 | 20 | let temp = Math.random() * 100; 21 | let humid = Math.random() * 100; 22 | 23 | log.info({ 24 | temp, 25 | humid, 26 | time: moment().subtract(d, "days").add(h, "hours").utc().format() 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lessons/04-raspberry/hw/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hw", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "balanced-match": { 8 | "version": "1.0.0", 9 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 10 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 11 | "optional": true 12 | }, 13 | "brace-expansion": { 14 | "version": "1.1.11", 15 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 16 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 17 | "optional": true, 18 | "requires": { 19 | "balanced-match": "^1.0.0", 20 | "concat-map": "0.0.1" 21 | } 22 | }, 23 | "bunyan": { 24 | "version": "1.8.12", 25 | "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", 26 | "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", 27 | "requires": { 28 | "dtrace-provider": "~0.8", 29 | "moment": "^2.10.6", 30 | "mv": "~2", 31 | "safe-json-stringify": "~1" 32 | } 33 | }, 34 | "concat-map": { 35 | "version": "0.0.1", 36 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 37 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 38 | "optional": true 39 | }, 40 | "dtrace-provider": { 41 | "version": "0.8.8", 42 | "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", 43 | "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", 44 | "optional": true, 45 | "requires": { 46 | "nan": "^2.14.0" 47 | } 48 | }, 49 | "glob": { 50 | "version": "6.0.4", 51 | "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", 52 | "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", 53 | "optional": true, 54 | "requires": { 55 | "inflight": "^1.0.4", 56 | "inherits": "2", 57 | "minimatch": "2 || 3", 58 | "once": "^1.3.0", 59 | "path-is-absolute": "^1.0.0" 60 | } 61 | }, 62 | "inflight": { 63 | "version": "1.0.6", 64 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 65 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 66 | "optional": true, 67 | "requires": { 68 | "once": "^1.3.0", 69 | "wrappy": "1" 70 | } 71 | }, 72 | "inherits": { 73 | "version": "2.0.4", 74 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 75 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 76 | "optional": true 77 | }, 78 | "minimatch": { 79 | "version": "3.0.4", 80 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 81 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 82 | "optional": true, 83 | "requires": { 84 | "brace-expansion": "^1.1.7" 85 | } 86 | }, 87 | "minimist": { 88 | "version": "0.0.8", 89 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 90 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 91 | "optional": true 92 | }, 93 | "mkdirp": { 94 | "version": "0.5.1", 95 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 96 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 97 | "optional": true, 98 | "requires": { 99 | "minimist": "0.0.8" 100 | } 101 | }, 102 | "moment": { 103 | "version": "2.24.0", 104 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", 105 | "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" 106 | }, 107 | "mv": { 108 | "version": "2.1.1", 109 | "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", 110 | "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", 111 | "optional": true, 112 | "requires": { 113 | "mkdirp": "~0.5.1", 114 | "ncp": "~2.0.0", 115 | "rimraf": "~2.4.0" 116 | } 117 | }, 118 | "nan": { 119 | "version": "2.14.0", 120 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", 121 | "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", 122 | "optional": true 123 | }, 124 | "ncp": { 125 | "version": "2.0.0", 126 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 127 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", 128 | "optional": true 129 | }, 130 | "once": { 131 | "version": "1.4.0", 132 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 133 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 134 | "optional": true, 135 | "requires": { 136 | "wrappy": "1" 137 | } 138 | }, 139 | "path-is-absolute": { 140 | "version": "1.0.1", 141 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 142 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 143 | "optional": true 144 | }, 145 | "rimraf": { 146 | "version": "2.4.5", 147 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", 148 | "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", 149 | "optional": true, 150 | "requires": { 151 | "glob": "^6.0.1" 152 | } 153 | }, 154 | "safe-json-stringify": { 155 | "version": "1.2.0", 156 | "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", 157 | "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", 158 | "optional": true 159 | }, 160 | "wrappy": { 161 | "version": "1.0.2", 162 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 163 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 164 | "optional": true 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /lessons/04-raspberry/hw/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hw", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "bunyan": "^1.8.12", 13 | "moment": "^2.24.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lessons/04-raspberry/img/circuit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/04-raspberry/img/circuit.jpg -------------------------------------------------------------------------------- /lessons/04-raspberry/img/hostname-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/04-raspberry/img/hostname-config.png -------------------------------------------------------------------------------- /lessons/04-raspberry/img/installing-os-part1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/04-raspberry/img/installing-os-part1.png -------------------------------------------------------------------------------- /lessons/05-midterm/05-midterm.md: -------------------------------------------------------------------------------- 1 | # Midterm - Putting it All Together 2 | 3 | ## Authors 4 | Sam Tarakajian for NYU IDM 5 | 6 | DM-GY 9103 7 | 8 | @starakaj 9 | 10 | ## Essential Questions 11 | - How does remote data gathering work? 12 | - How do large teams work on software together? 13 | - How do multiple people collaborate on software projects? 14 | - How do you keep a large software project organized? 15 | - What kind of data can a Raspberry Pi gather? How? 16 | - How can that data be presented in a useful way? 17 | 18 | ## Introduction 19 | We've been building skills for weeks now, it's time to put them to the test. We've seen how to build a web server that exposes an API for creating, modifying and deleting data. We've programmed a Raspberry Pi to gather data and upload it remotely. Finally, we've built a front end that presents data from the server in an illustrative, attractive format. These are all the pieces of a complete, end-to-end software application. 20 | 21 | What we haven't done yet is put all those pieces together. Rather than have each person build the whole application, we're going to divide the project up and work on the whole thing as a class. Hopefully the experience will give everyone a flavor for what it's like to work on a large software application, and hopefully it will be fun. 22 | 23 | ### Target Audience / Prerequisite & Pre-Assessment 24 | This module is part of DM-GY 9103, _Programming is the Art of the Possible_. This is a second semester creative coding course, designed for students who have a strong JavaScript foundation. 25 | 26 | ### Outcomes & Goals 27 | * In working on this midterm assignment, we'll be working on three major parts of a large software application, a user-facing frontend, a data-persisting backend, and a data-gathering piece of custom hardware. 28 | * Through this experience, we'll learn how to coordinate a large project among many people. 29 | 30 | ## Midterm Description 31 | See the full [midterm description](../../homework/midterm.md) for details. 32 | 33 | ### References 34 | TBD 35 | 36 | ### Implementation Guidance & Teaching Reflection 37 | TBD 38 | 39 | ***With thanks and acknowledgement, this is based on the template provided by [Eyebeam](https://github.com/eyebeam/curriculum/blob/master/TEMPLATE.md)*** -------------------------------------------------------------------------------- /lessons/06-osc/img/appkettle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/appkettle.png -------------------------------------------------------------------------------- /lessons/06-osc/img/basic-envelope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/basic-envelope.png -------------------------------------------------------------------------------- /lessons/06-osc/img/osc-controlling-sequencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/osc-controlling-sequencer.png -------------------------------------------------------------------------------- /lessons/06-osc/img/osc-multiple-parameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/osc-multiple-parameters.png -------------------------------------------------------------------------------- /lessons/06-osc/img/osc-trigger-env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/osc-trigger-env.png -------------------------------------------------------------------------------- /lessons/06-osc/img/oscillator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/oscillator.png -------------------------------------------------------------------------------- /lessons/06-osc/img/processing-osc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/processing-osc.png -------------------------------------------------------------------------------- /lessons/06-osc/img/sharks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/sharks.jpg -------------------------------------------------------------------------------- /lessons/06-osc/img/squares.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/squares.png -------------------------------------------------------------------------------- /lessons/06-osc/img/unimpressive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/06-osc/img/unimpressive.png -------------------------------------------------------------------------------- /lessons/06-osc/patcher/silly-dumb-node-thing/.gitignore: -------------------------------------------------------------------------------- 1 | code/silly/* -------------------------------------------------------------------------------- /lessons/06-osc/patcher/silly-dumb-node-thing/code/silly-dumb.js: -------------------------------------------------------------------------------- 1 | const maxApi = require("max-api"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | 5 | let sourceDirectory = null; 6 | 7 | let directories = []; 8 | 9 | maxApi.addHandler("init", async (dir) => { 10 | sourceDirectory = dir; 11 | 12 | for (let i = 0; i < 10; i++) { 13 | const fn = path.join(__dirname, sourceDirectory, `${i}-**********`); 14 | fs.writeFile(fn, "lol", (err) => { 15 | console.error(err); 16 | }); 17 | directories[i] = fn; 18 | } 19 | }); 20 | 21 | maxApi.addHandler("values", async (...values) => { 22 | let todos = []; 23 | values.forEach((v, i) => { 24 | let idx = Math.floor(((v / 2) + 0.5) * 10); 25 | if (idx >= 10) idx = 9; 26 | let str = ""; 27 | for (let c = 0; c <= idx; c++) { 28 | str += c === idx ? "|" : "*"; 29 | } 30 | const newFileName = `${i}-${str}`; 31 | const fn = path.join(__dirname, sourceDirectory, newFileName); 32 | todos.push(new Promise((resolve) => { 33 | fs.rename(directories[i], fn, resolve); 34 | })); 35 | directories[i] = fn; 36 | }); 37 | 38 | await Promise.all(todos); 39 | }); 40 | -------------------------------------------------------------------------------- /lessons/06-osc/patcher/silly-dumb-node-thing/silly-dumb-node-thing.maxproj: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "silly-dumb-node-thing", 3 | "version" : 1, 4 | "creationdate" : 3666698570, 5 | "modificationdate" : 3666699308, 6 | "viewrect" : [ 25.0, 104.0, 300.0, 500.0 ], 7 | "autoorganize" : 0, 8 | "hideprojectwindow" : 0, 9 | "showdependencies" : 1, 10 | "autolocalize" : 0, 11 | "contents" : { 12 | "patchers" : { 13 | "silly-dumb-node-thing.maxpat" : { 14 | "kind" : "patcher", 15 | "local" : 1, 16 | "toplevel" : 1 17 | } 18 | 19 | } 20 | , 21 | "code" : { 22 | "silly-dumb.js" : { 23 | "kind" : "javascript", 24 | "local" : 1 25 | } 26 | 27 | } 28 | 29 | } 30 | , 31 | "layout" : { 32 | 33 | } 34 | , 35 | "searchpath" : { 36 | 37 | } 38 | , 39 | "detailsvisible" : 0, 40 | "amxdtype" : 1633771873, 41 | "readonly" : 0, 42 | "devpathtype" : 0, 43 | "devpath" : ".", 44 | "sortmode" : 0, 45 | "viewmode" : 0 46 | } 47 | -------------------------------------------------------------------------------- /lessons/06-osc/processing/patatap_osc/patatap_osc.pde: -------------------------------------------------------------------------------- 1 | import netP5.*; 2 | import oscP5.*; 3 | 4 | // Create a global variable to store our oscP5 server 5 | OscP5 oscP5; 6 | 7 | // The list of rectangles that we want to draw 8 | ArrayList rectangles = new ArrayList(); 9 | 10 | void setup() { 11 | size(1080, 720); 12 | 13 | // Make an oscP5 server that listens on port 9001 14 | oscP5 = new OscP5(this, 9001); 15 | 16 | // this binds the address /createRectangle to the function createRectangle 17 | oscP5.plug(this, "createRectangle", "/createRectangle"); 18 | } 19 | 20 | synchronized void createRectangle(float x, float y, float w, float h) { 21 | float[] newRectangle = {x, y, w, h}; 22 | rectangles.add(newRectangle); 23 | } 24 | 25 | synchronized void draw() { 26 | 27 | background(51); 28 | 29 | for (float[] rectangle : rectangles) { 30 | 31 | strokeWeight(2); 32 | stroke(0); 33 | 34 | // Just for fun, we can highlight any rectangles that intersect the mouse 35 | if (mouseX > rectangle[0] 36 | && mouseX < rectangle[0] + rectangle[2] 37 | && mouseY > rectangle[1] 38 | && mouseY < rectangle[1] + rectangle[3]) 39 | { 40 | fill(245, 66, 221); 41 | } else { 42 | fill(255); 43 | } 44 | 45 | rect(rectangle[0], rectangle[1], rectangle[2], rectangle[3]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lessons/06-osc/server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | 3 | // Look at this object destructuring 4 | const { Client } = require("node-osc"); 5 | 6 | // 127.0.0.1 is localhost, meaning this same machine. 9001 is the port we want to listen on. 7 | const oscClient = new Client("127.0.0.1", "9001"); 8 | 9 | const app = express(); 10 | 11 | // We don't have anything to do at the root, so we may as well redirect to /randomRectangle 12 | app.get("/", (req, res) => { 13 | res.redirect("/randomRectangle"); 14 | }); 15 | 16 | // Send an OSC message to processing that creates a random rectangle 17 | app.get("/randomRectangle", (req, res) => { 18 | 19 | // Create random dimensions that will fit the rectangle on the screen 20 | const randomWidth = Math.random() * 250; 21 | const randomHeight = Math.random() * 250; 22 | const randomX = Math.random() * (1080 - randomWidth); 23 | const randomY = Math.random() * (720 - randomHeight); 24 | 25 | // Send an OSC packet 26 | oscClient.send("/createRectangle", randomX, randomY, randomWidth, randomHeight, () => { 27 | 28 | // Send back status when the packet gets sent 29 | res.status(200).send(`Added a rectangle: 30 | x: ${randomX.toFixed(1)} 31 | y: ${randomY.toFixed(1)} 32 | width: ${randomWidth.toFixed(1)} 33 | height: ${randomHeight.toFixed(1)}`); 34 | }); 35 | }); 36 | 37 | app.listen(3000, () => { 38 | console.log("App listening on port 3000"); 39 | }); 40 | -------------------------------------------------------------------------------- /lessons/06-osc/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.1", 13 | "node-osc": "^4.1.7" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lessons/07-audio/img/attribution.txt: -------------------------------------------------------------------------------- 1 | spectrogram_of_violin.txt - retrieved from https://commons.wikimedia.org/wiki/File:Spectrogram_of_violin.png -------------------------------------------------------------------------------- /lessons/07-audio/img/audio-context-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/07-audio/img/audio-context-warning.png -------------------------------------------------------------------------------- /lessons/07-audio/img/spectrogram_of_violin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/07-audio/img/spectrogram_of_violin.png -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js 2 | public/*.mp3 3 | node_modules -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jenn Schiffer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/README.md: -------------------------------------------------------------------------------- 1 | Starter Express App on Glitch 2 | =========================== 3 | 4 | This app is a very small scaffold to get you started using Express and Webpack. 5 | 6 | It's been copied by @starakaj for your enjoyment, and then React has been removed You can find the original at https://glitch.com/~starter-react. 7 | 8 | This project relates to video 2 of 5 in the [React Starter Kit](https://glitch.com/react-starter-kit) video series. 9 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Audio Visualizer Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-react", 3 | "version": "0.0.1", 4 | "description": "A small scaffold to get you started using React with Webpack", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack && node server.js", 9 | "watch": "NODE_ENV=development node server.js" 10 | }, 11 | "dependencies": { 12 | "dat.gui": "^0.7.6", 13 | "express": "^4.16.4", 14 | "jsx-loader": "^0.13.2", 15 | "meyda": "^4.3.1", 16 | "p5": "^1.0.0", 17 | "react": "^16.8.6", 18 | "react-dom": "^16.8.6", 19 | "startaudiocontext": "^1.2.1", 20 | "webpack": "^4.30.0", 21 | "webpack-cli": "^3.3.0", 22 | "webpack-dev-middleware": "^3.7.2", 23 | "webpack-dev-server": "^3.10.3" 24 | }, 25 | "engines": { 26 | "node": "12.x" 27 | }, 28 | "repository": { 29 | "url": "https://glitch.com/edit/#!/starter-react" 30 | }, 31 | "license": "MIT", 32 | "keywords": [ 33 | "node", 34 | "glitch", 35 | "express" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Futura; 3 | } -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // init project 4 | const express = require('express'); 5 | const app = express(); 6 | const port = process.env.PORT || 3000; 7 | 8 | // Special piece for running with webpack dev server 9 | if (process.env.NODE_ENV === "development") { 10 | const webpack = require('webpack'); 11 | const webpackDevMiddleware = require('webpack-dev-middleware'); 12 | const config = require('./webpack.dev.config.js'); 13 | const compiler = webpack(config); 14 | 15 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 16 | // configuration file as a base. 17 | app.use(webpackDevMiddleware(compiler, { 18 | publicPath: config.output.publicPath, 19 | })); 20 | } 21 | 22 | // http://expressjs.com/en/starter/static-files.html 23 | app.use(express.static('public')); 24 | 25 | // http://expressjs.com/en/starter/basic-routing.html 26 | app.get("/", function(request, response) { 27 | response.sendFile(__dirname + '/app/index.html'); 28 | }); 29 | 30 | // listen for requests :) 31 | const listener = app.listen(port, function () { 32 | console.log('Your app is listening on port ' + port); 33 | }); 34 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | output: { 8 | path: path.join(__dirname, 'public'), 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.jsx'], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?$/, 18 | loader: 'jsx-loader', 19 | exclude: /node_modules/, 20 | include: path.join(__dirname, 'app'), 21 | }, 22 | ], 23 | }, 24 | }; -------------------------------------------------------------------------------- /lessons/07-audio/meyda-app/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'bundle.js', 11 | publicPath: '/', 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.jsx'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.jsx?$/, 20 | loader: 'jsx-loader', 21 | exclude: /node_modules/, 22 | include: path.join(__dirname, 'app'), 23 | }, 24 | ], 25 | }, 26 | devServer: { 27 | contentBase: path.join(__dirname, 'public'), 28 | port: 3000 29 | } 30 | }; -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js 2 | public/*.mp3 3 | node_modules -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jenn Schiffer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/README.md: -------------------------------------------------------------------------------- 1 | Starter React App on Glitch 2 | =========================== 3 | 4 | This app is a very small scaffold to get you started using React and Webpack. 5 | 6 | It's been copied by @starakaj for your enjoyment. You can find the original at https://glitch.com/~starter-react. 7 | 8 | This project relates to video 2 of 5 in the [React Starter Kit](https://glitch.com/react-starter-kit) video series. 9 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/app/app.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const ReactDOM = require("react-dom"); 3 | 4 | /* Import Components */ 5 | const RootComponent = require("./components/RootComponent"); 6 | 7 | ReactDOM.render(, document.getElementById("main")); -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/app/components/ClockFace.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | 3 | function ClockFace(props) { 4 | 5 | const [date, setDate] = React.useState(new Date()); 6 | 7 | // This will be called whenever the component renders, but because we pass an empty 8 | // array as the second argument, it will only be called once, when the component 9 | // first renders. 10 | React.useEffect(() => { 11 | 12 | const timerId = setInterval(() => { 13 | setDate(new Date); 14 | }, 1000); 15 | 16 | // By returning a function from useEffect, we tell React that we'd like this 17 | // function called when the component is unmounted 18 | return () => { clearInterval(timerId) }; 19 | 20 | }, []); 21 | 22 | let prefix = ""; 23 | let postfix = "" 24 | if (props.language === "en") { 25 | prefix = "It is"; 26 | postfix = "o'clock"; 27 | } else if (props.language === "fr") { 28 | prefix = "Il est"; 29 | postfix = "heures"; 30 | } 31 | 32 | return ( 33 |

{prefix} {date.getHours()}:{date.getMinutes()}:{date.getSeconds()} {postfix}

34 | ); 35 | } 36 | 37 | module.exports = ClockFace; -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/app/components/RootComponent.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const ClockFace = require("./ClockFace"); 3 | 4 | const Meyda = require("meyda"); 5 | 6 | /* the main page for the index route of this app */ 7 | const RootComponent = function() { 8 | 9 | const audioRef = React.useRef(); 10 | const drawingRef = React.useRef(); 11 | 12 | const [loudness, setLoudness] = React.useState([]); 13 | 14 | const audioEffect = React.useEffect(() => { 15 | const audioContext = new AudioContext(); 16 | const audioElement = audioRef.current; 17 | const source = audioContext.createMediaElementSource(audioElement); 18 | source.connect(audioContext.destination); 19 | 20 | const analyzer = Meyda.createMeydaAnalyzer({ 21 | audioContext: audioContext, 22 | source: source, 23 | bufferSize: 512, 24 | featureExtractors: ["loudness"], 25 | callback: (features) => { 26 | setLoudness(features.loudness) 27 | } 28 | }); 29 | 30 | analyzer.start(); 31 | }, []); 32 | 33 | const drawingEffect = React.useEffect(() => { 34 | const canvas = drawingRef.current; 35 | if (canvas && loudness.specific.length) { 36 | const ctx = canvas.getContext("2d"); 37 | 38 | 39 | } 40 | 41 | }, [loudness]); 42 | 43 | return ( 44 |
45 | 53 | 54 | 55 |
56 | ); 57 | } 58 | 59 | module.exports = RootComponent; -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Starter React 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-react", 3 | "version": "0.0.1", 4 | "description": "A small scaffold to get you started using React with Webpack", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack && node server.js", 9 | "watch": "NODE_ENV=development node server.js" 10 | }, 11 | "dependencies": { 12 | "express": "^4.16.4", 13 | "jsx-loader": "^0.13.2", 14 | "meyda": "^4.3.1", 15 | "react": "^16.8.6", 16 | "react-dom": "^16.8.6", 17 | "webpack": "^4.30.0", 18 | "webpack-cli": "^3.3.0", 19 | "webpack-dev-middleware": "^3.7.2", 20 | "webpack-dev-server": "^3.10.3" 21 | }, 22 | "engines": { 23 | "node": "12.x" 24 | }, 25 | "repository": { 26 | "url": "https://glitch.com/edit/#!/starter-react" 27 | }, 28 | "license": "MIT", 29 | "keywords": [ 30 | "node", 31 | "glitch", 32 | "express" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Futura; 3 | } -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // init project 4 | const express = require('express'); 5 | const app = express(); 6 | const port = process.env.PORT || 3000; 7 | 8 | // Special piece for running with webpack dev server 9 | if (process.env.NODE_ENV === "development") { 10 | const webpack = require('webpack'); 11 | const webpackDevMiddleware = require('webpack-dev-middleware'); 12 | const config = require('./webpack.dev.config.js'); 13 | const compiler = webpack(config); 14 | 15 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 16 | // configuration file as a base. 17 | app.use(webpackDevMiddleware(compiler, { 18 | publicPath: config.output.publicPath, 19 | })); 20 | } 21 | 22 | // http://expressjs.com/en/starter/static-files.html 23 | app.use(express.static('public')); 24 | 25 | // http://expressjs.com/en/starter/basic-routing.html 26 | app.get("/", function(request, response) { 27 | response.sendFile(__dirname + '/app/index.html'); 28 | }); 29 | 30 | // listen for requests :) 31 | const listener = app.listen(port, function () { 32 | console.log('Your app is listening on port ' + port); 33 | }); 34 | -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.jsx', 7 | output: { 8 | path: path.join(__dirname, 'public'), 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.jsx'], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?$/, 18 | loader: 'jsx-loader', 19 | exclude: /node_modules/, 20 | include: path.join(__dirname, 'app'), 21 | }, 22 | ], 23 | }, 24 | }; -------------------------------------------------------------------------------- /lessons/07-audio/meyda-demo/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.jsx', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'bundle.js', 11 | publicPath: '/', 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.jsx'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.jsx?$/, 20 | loader: 'jsx-loader', 21 | exclude: /node_modules/, 22 | include: path.join(__dirname, 'app'), 23 | }, 24 | ], 25 | }, 26 | devServer: { 27 | contentBase: path.join(__dirname, 'public'), 28 | port: 3000 29 | } 30 | }; -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js 2 | node_modules -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jenn Schiffer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/README.md: -------------------------------------------------------------------------------- 1 | Starter React App on Glitch 2 | =========================== 3 | 4 | This app is a very small scaffold to get you started using React and Webpack. 5 | 6 | It's been copied by @starakaj for your enjoyment. You can find the original at https://glitch.com/~starter-react. 7 | 8 | This project relates to video 2 of 5 in the [React Starter Kit](https://glitch.com/react-starter-kit) video series. 9 | -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/app/app.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const ReactDOM = require("react-dom"); 3 | 4 | /* Import Components */ 5 | const RootComponent = require("./components/RootComponent"); 6 | 7 | ReactDOM.render(, document.getElementById("main")); -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/app/components/ClockFace.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const Tone = require("tone"); 3 | 4 | function ClockFace(props) { 5 | 6 | const [date, setDate] = React.useState(new Date()); 7 | 8 | // This will be called whenever the component renders, but because we pass an empty 9 | // array as the second argument, it will only be called once, when the component 10 | // first renders. 11 | React.useEffect(() => { 12 | 13 | const timerId = setInterval(() => { 14 | setDate(new Date); 15 | 16 | // Fetch the synth from props, and play a note each second 17 | props.synth.triggerAttackRelease('C4', '16n'); 18 | }, 1000); 19 | 20 | // By returning a function from useEffect, we tell React that we'd like this 21 | // function called when the component is unmounted 22 | return () => { clearInterval(timerId) }; 23 | 24 | }, []); 25 | 26 | let prefix = ""; 27 | let postfix = "" 28 | if (props.language === "en") { 29 | prefix = "It is"; 30 | postfix = "o'clock"; 31 | } else if (props.language === "fr") { 32 | prefix = "Il est"; 33 | postfix = "heures"; 34 | } 35 | 36 | return ( 37 |

{prefix} {date.getHours()}:{date.getMinutes()}:{date.getSeconds()} {postfix}

38 | ); 39 | } 40 | 41 | module.exports = ClockFace; -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/app/components/RootComponent.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const ClockFace = require("./ClockFace"); 3 | const Tone = require("tone"); 4 | const StartAudioContext = require("startaudiocontext"); // 1 5 | 6 | const synth = new Tone.FMSynth().toMaster(); 7 | StartAudioContext(Tone.context); // 2 8 | 9 | /* the main page for the index route of this app */ 10 | const RootComponent = function() { 11 | 12 | const handleMouseMove = (mouseevent) => { 13 | synth.modulationIndex.rampTo(100 * mouseevent.clientX / window.innerWidth); 14 | }; 15 | 16 | return ( 17 |
18 |

Hello!

19 | 20 |

Your app here

21 | 22 | 23 |
24 | ); 25 | } 26 | 27 | module.exports = RootComponent; -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Starter React 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-react", 3 | "version": "0.0.1", 4 | "description": "A small scaffold to get you started using React with Webpack", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack && node server.js", 9 | "watch": "NODE_ENV=development node server.js" 10 | }, 11 | "dependencies": { 12 | "express": "^4.16.4", 13 | "jsx-loader": "^0.13.2", 14 | "react": "^16.8.6", 15 | "react-dom": "^16.8.6", 16 | "startaudiocontext": "^1.2.1", 17 | "tone": "^13.8.25", 18 | "webpack": "^4.30.0", 19 | "webpack-cli": "^3.3.0", 20 | "webpack-dev-middleware": "^3.7.2", 21 | "webpack-dev-server": "^3.10.3" 22 | }, 23 | "engines": { 24 | "node": "12.x" 25 | }, 26 | "repository": { 27 | "url": "https://glitch.com/edit/#!/starter-react" 28 | }, 29 | "license": "MIT", 30 | "keywords": [ 31 | "node", 32 | "glitch", 33 | "express" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Futura; 3 | } 4 | 5 | .root { 6 | position: absolute; 7 | background-color: palevioletred; 8 | top: 0; 9 | left: 0; 10 | width: 100%; 11 | height: 100vh; 12 | padding-left: 20px; 13 | } -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // init project 4 | const express = require('express'); 5 | const app = express(); 6 | const port = process.env.PORT || 3000; 7 | 8 | // Special piece for running with webpack dev server 9 | if (process.env.NODE_ENV === "development") { 10 | const webpack = require('webpack'); 11 | const webpackDevMiddleware = require('webpack-dev-middleware'); 12 | const config = require('./webpack.dev.config.js'); 13 | const compiler = webpack(config); 14 | 15 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 16 | // configuration file as a base. 17 | app.use(webpackDevMiddleware(compiler, { 18 | publicPath: config.output.publicPath, 19 | })); 20 | } 21 | 22 | // http://expressjs.com/en/starter/static-files.html 23 | app.use(express.static('public')); 24 | 25 | // http://expressjs.com/en/starter/basic-routing.html 26 | app.get("/", function(request, response) { 27 | response.sendFile(__dirname + '/app/index.html'); 28 | }); 29 | 30 | // listen for requests :) 31 | const listener = app.listen(port, function () { 32 | console.log('Your app is listening on port ' + port); 33 | }); 34 | -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.jsx', 7 | output: { 8 | path: path.join(__dirname, 'public'), 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.jsx'], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?$/, 18 | loader: 'jsx-loader', 19 | exclude: /node_modules/, 20 | include: path.join(__dirname, 'app'), 21 | }, 22 | ], 23 | }, 24 | }; -------------------------------------------------------------------------------- /lessons/07-audio/tonetest/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.jsx', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'bundle.js', 11 | publicPath: '/', 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.jsx'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.jsx?$/, 20 | loader: 'jsx-loader', 21 | exclude: /node_modules/, 22 | include: path.join(__dirname, 'app'), 23 | }, 24 | ], 25 | }, 26 | devServer: { 27 | contentBase: path.join(__dirname, 'public'), 28 | port: 3000 29 | } 30 | }; -------------------------------------------------------------------------------- /lessons/08-video/flow-app/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js 2 | node_modules -------------------------------------------------------------------------------- /lessons/08-video/flow-app/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jenn Schiffer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lessons/08-video/flow-app/Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /lessons/08-video/flow-app/README.md: -------------------------------------------------------------------------------- 1 | Starter Express App on Glitch 2 | =========================== 3 | 4 | This app is a very small scaffold to get you started using Express and Webpack. 5 | 6 | It's been copied by @starakaj for your enjoyment, and then React has been removed You can find the original at https://glitch.com/~starter-react. 7 | 8 | This project relates to video 2 of 5 in the [React Starter Kit](https://glitch.com/react-starter-kit) video series. 9 | -------------------------------------------------------------------------------- /lessons/08-video/flow-app/app/app.js: -------------------------------------------------------------------------------- 1 | const FlowCalculator = require("./flow"); 2 | const p5 = require("p5"); 3 | 4 | class Particle { 5 | constructor(x, y, vx, vy) { 6 | this.x = x; 7 | this.y = y; 8 | this.vx = vx; 9 | this.vy = vy; 10 | } 11 | 12 | draw(p) { 13 | p.strokeWeight(0); 14 | p.fill(255); 15 | p.ellipse(this.x, this.y, 2, 2); 16 | } 17 | 18 | update() { 19 | this.x += this.vx; 20 | this.y += this.vy; 21 | this.vy += 1; 22 | } 23 | 24 | outOfBounds(p) { 25 | return this.x < 0 || 26 | this.x > p.width || 27 | this.y > p.height; 28 | } 29 | } 30 | 31 | const p5Flow = (p) => { 32 | // https://kylemcdonald.github.io/cv-examples/ 33 | 34 | var capture; 35 | var previousPixels; 36 | var flow; 37 | var w = 640, 38 | h = 480; 39 | var step = 6; 40 | let particles = []; 41 | 42 | var uMotionGraph, vMotionGraph; 43 | 44 | p.setup = () => { 45 | p.createCanvas(w, h); 46 | capture = p.createCapture({ 47 | audio: false, 48 | video: { 49 | width: w, 50 | height: h 51 | } 52 | }, function() { 53 | console.log('capture ready.') 54 | }); 55 | capture.elt.setAttribute('playsinline', ''); 56 | capture.hide(); 57 | flow = new FlowCalculator(step); 58 | // uMotionGraph = new Graph(100, -step / 2, +step / 2); 59 | // vMotionGraph = new Graph(100, -step / 2, +step / 2); 60 | } 61 | 62 | function copyImage(src, dst) { 63 | var n = src.length; 64 | if (!dst || dst.length != n) dst = new src.constructor(n); 65 | while (n--) dst[n] = src[n]; 66 | return dst; 67 | } 68 | 69 | function same(a1, a2, stride, n) { 70 | for (var i = 0; i < n; i += stride) { 71 | if (a1[i] != a2[i]) { 72 | return false; 73 | } 74 | } 75 | return true; 76 | } 77 | 78 | p.draw = () => { 79 | capture.loadPixels(); 80 | if (capture.pixels.length > 0) { 81 | if (previousPixels) { 82 | 83 | // cheap way to ignore duplicate frames 84 | if (same(previousPixels, capture.pixels, 4, p.width)) { 85 | return; 86 | } 87 | 88 | flow.calculate(previousPixels, capture.pixels, capture.width, capture.height); 89 | } 90 | previousPixels = copyImage(capture.pixels, previousPixels); 91 | p.image(capture, 0, 0, w, h); 92 | 93 | if (flow.flow && flow.flow.u != 0 && flow.flow.v != 0) { 94 | // uMotionGraph.addSample(flow.flow.u); 95 | // vMotionGraph.addSample(flow.flow.v); 96 | 97 | p.strokeWeight(2); 98 | flow.flow.zones.forEach(function(zone) { 99 | p.stroke(p.map(zone.u, -step, +step, 0, 255), 100 | p.map(zone.v, -step, +step, 0, 255), 128); 101 | // p.line(zone.x, zone.y, zone.x + zone.u, zone.y + zone.v); 102 | 103 | // maybe add a particle 104 | // this is a lazy way to calculate velocity 105 | if (Math.abs(zone.u) + Math.abs(zone.v) > 10 && particles.length < 500) { 106 | particles.push(new Particle(zone.x, zone.y, zone.u, zone.v)); 107 | } 108 | }); 109 | } 110 | 111 | particles = particles.filter(part => !part.outOfBounds(p)); 112 | particles.forEach(part => part.draw(p)); 113 | particles.forEach(part => part.update(p)); 114 | } 115 | } 116 | } 117 | 118 | const myp5 = new p5(p5Flow, "main"); 119 | -------------------------------------------------------------------------------- /lessons/08-video/flow-app/app/flow.js: -------------------------------------------------------------------------------- 1 | // copied from https://github.com/anvaka/oflow 2 | 3 | class FlowZone{ 4 | constructor (x, y, u, v) { 5 | this.x = x; 6 | this.y = y; 7 | this.u = u; 8 | this.v = v; 9 | } 10 | } 11 | 12 | class FlowCalculator { 13 | constructor(step = 8) { 14 | this.step = step; 15 | } 16 | 17 | // assumes rgba images, but only uses one channel 18 | calculate (oldImage, newImage, width, height) { 19 | var zones = []; 20 | var step = this.step; 21 | var winStep = step * 2 + 1; 22 | 23 | var A2, A1B2, B1, C1, C2; 24 | var u, v, uu, vv; 25 | uu = vv = 0; 26 | var wMax = width - step - 1; 27 | var hMax = height - step - 1; 28 | var globalY, globalX, localY, localX; 29 | 30 | for (globalY = step + 1; globalY < hMax; globalY += winStep) { 31 | for (globalX = step + 1; globalX < wMax; globalX += winStep) { 32 | A2 = A1B2 = B1 = C1 = C2 = 0; 33 | 34 | for (localY = -step; localY <= step; localY++) { 35 | for (localX = -step; localX <= step; localX++) { 36 | var address = (globalY + localY) * width + globalX + localX; 37 | 38 | var gradX = (newImage[(address - 1) * 4]) - (newImage[(address + 1) * 4]); 39 | var gradY = (newImage[(address - width) * 4]) - (newImage[(address + width) * 4]); 40 | var gradT = (oldImage[address * 4]) - (newImage[address * 4]); 41 | 42 | A2 += gradX * gradX; 43 | A1B2 += gradX * gradY; 44 | B1 += gradY * gradY; 45 | C2 += gradX * gradT; 46 | C1 += gradY * gradT; 47 | } 48 | } 49 | 50 | var delta = (A1B2 * A1B2 - A2 * B1); 51 | 52 | if (delta !== 0) { 53 | /* system is not singular - solving by Kramer method */ 54 | var Idelta = step / delta; 55 | var deltaX = -(C1 * A1B2 - C2 * B1); 56 | var deltaY = -(A1B2 * C2 - A2 * C1); 57 | 58 | u = deltaX * Idelta; 59 | v = deltaY * Idelta; 60 | } else { 61 | /* singular system - find optical flow in gradient direction */ 62 | var norm = (A1B2 + A2) * (A1B2 + A2) + (B1 + A1B2) * (B1 + A1B2); 63 | if (norm !== 0) { 64 | var IGradNorm = step / norm; 65 | var temp = -(C1 + C2) * IGradNorm; 66 | 67 | u = (A1B2 + A2) * temp; 68 | v = (B1 + A1B2) * temp; 69 | } else { 70 | u = v = 0; 71 | } 72 | } 73 | 74 | if (-winStep < u && u < winStep && 75 | -winStep < v && v < winStep) { 76 | uu += u; 77 | vv += v; 78 | zones.push(new FlowZone(globalX, globalY, u, v)); 79 | } 80 | } 81 | } 82 | 83 | this.flow = { 84 | zones : zones, 85 | u : uu / zones.length, 86 | v : vv / zones.length 87 | }; 88 | 89 | return this.flow; 90 | }; 91 | }; 92 | 93 | module.exports = FlowCalculator; 94 | -------------------------------------------------------------------------------- /lessons/08-video/flow-app/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Starter React 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lessons/08-video/flow-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-react", 3 | "version": "0.0.1", 4 | "description": "A small scaffold to get you started using React with Webpack", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack && node server.js", 9 | "watch": "NODE_ENV=development node server.js" 10 | }, 11 | "dependencies": { 12 | "express": "^4.16.4", 13 | "jsx-loader": "^0.13.2", 14 | "p5": "^1.0.0", 15 | "react": "^16.8.6", 16 | "react-dom": "^16.8.6", 17 | "webpack": "^4.30.0", 18 | "webpack-cli": "^3.3.0", 19 | "webpack-dev-middleware": "^3.7.2", 20 | "webpack-dev-server": "^3.10.3" 21 | }, 22 | "engines": { 23 | "node": "12.x" 24 | }, 25 | "repository": { 26 | "url": "https://glitch.com/edit/#!/starter-react" 27 | }, 28 | "license": "MIT", 29 | "keywords": [ 30 | "node", 31 | "glitch", 32 | "express" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /lessons/08-video/flow-app/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Futura; 3 | } -------------------------------------------------------------------------------- /lessons/08-video/flow-app/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // init project 4 | const express = require('express'); 5 | const app = express(); 6 | const port = process.env.PORT || 3000; 7 | 8 | // Special piece for running with webpack dev server 9 | if (process.env.NODE_ENV === "development") { 10 | const webpack = require('webpack'); 11 | const webpackDevMiddleware = require('webpack-dev-middleware'); 12 | const config = require('./webpack.dev.config.js'); 13 | const compiler = webpack(config); 14 | 15 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 16 | // configuration file as a base. 17 | app.use(webpackDevMiddleware(compiler, { 18 | publicPath: config.output.publicPath, 19 | })); 20 | } 21 | 22 | // http://expressjs.com/en/starter/static-files.html 23 | app.use(express.static('public')); 24 | 25 | // http://expressjs.com/en/starter/basic-routing.html 26 | app.get("/", function(request, response) { 27 | response.sendFile(__dirname + '/app/index.html'); 28 | }); 29 | 30 | // listen for requests :) 31 | const listener = app.listen(port, function () { 32 | console.log('Your app is listening on port ' + port); 33 | }); 34 | -------------------------------------------------------------------------------- /lessons/08-video/flow-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | output: { 8 | path: path.join(__dirname, 'public'), 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.jsx'], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?$/, 18 | loader: 'jsx-loader', 19 | exclude: /node_modules/, 20 | include: path.join(__dirname, 'app'), 21 | }, 22 | ], 23 | }, 24 | }; -------------------------------------------------------------------------------- /lessons/08-video/flow-app/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'bundle.js', 11 | publicPath: '/', 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.jsx'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.jsx?$/, 20 | loader: 'jsx-loader', 21 | exclude: /node_modules/, 22 | include: path.join(__dirname, 'app'), 23 | }, 24 | ], 25 | }, 26 | devServer: { 27 | contentBase: path.join(__dirname, 'public'), 28 | port: 3000 29 | } 30 | }; -------------------------------------------------------------------------------- /lessons/08-video/img/attribution.txt: -------------------------------------------------------------------------------- 1 | illusion.jpg - By Kenjiro995 - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=33076513 -------------------------------------------------------------------------------- /lessons/08-video/img/illusion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/08-video/img/illusion.jpg -------------------------------------------------------------------------------- /lessons/08-video/img/kyle-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/08-video/img/kyle-files.png -------------------------------------------------------------------------------- /lessons/08-video/img/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/08-video/img/smile.png -------------------------------------------------------------------------------- /lessons/08-video/img/wave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/08-video/img/wave.png -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js 2 | node_modules -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jenn Schiffer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/README.md: -------------------------------------------------------------------------------- 1 | Starter Express App on Glitch 2 | =========================== 3 | 4 | This app is a very small scaffold to get you started using Express and Webpack. 5 | 6 | It's been copied by @starakaj for your enjoyment, and then React has been removed You can find the original at https://glitch.com/~starter-react. 7 | 8 | This project relates to video 2 of 5 in the [React Starter Kit](https://glitch.com/react-starter-kit) video series. 9 | -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/app/app.js: -------------------------------------------------------------------------------- 1 | // Overall idea: make sounds with your mouth. Open mouth to start playing sounds, 2 | // close mouth to stop sounds. Adjust the aperature of your mouth to change sound params. 3 | 4 | // 1. Get video 5 | // 2. Draw the video in the browser 6 | // 3. Set up the instrument 7 | // 4. Read parameters of the video to make sound 8 | 9 | const p5 = require("p5"); 10 | const Tone = require("tone"); 11 | const StartAudioContext = require("startaudiocontext"); 12 | 13 | let lastMouthState = false; 14 | const synth = new Tone.FMSynth().toMaster(); 15 | StartAudioContext(Tone.context); 16 | 17 | let faceapi; 18 | let detections; 19 | let width = 400; 20 | let height = 300; 21 | 22 | function handleMouthState(state) { 23 | if (state !== lastMouthState) { 24 | lastMouthState = state; 25 | if (state) { 26 | synth.triggerAttack("C4"); 27 | } else { 28 | synth.triggerRelease(); 29 | } 30 | } 31 | } 32 | 33 | function handleMouthWidth(width) { 34 | // Looks like mouth width varies between 0.4 and 0.23 35 | const widthMinimum = 0.23; 36 | const widthMaximum = 0.4; 37 | const normalizedWidth = (width - widthMinimum) / (widthMaximum - widthMinimum); 38 | synth.modulationIndex.rampTo(100 * normalizedWidth); 39 | } 40 | 41 | const p5draw = (p) => { 42 | 43 | let p5video; 44 | 45 | function drawBox(detections) { 46 | detections.forEach((detection) => { 47 | const alignedRect = detection.alignedRect; 48 | 49 | p.noFill(); 50 | p.stroke(255, 255, 255); 51 | p.strokeWeight(2); 52 | p.rect( 53 | alignedRect._box._x, 54 | alignedRect._box._y, 55 | alignedRect._box._width, 56 | alignedRect._box._height, 57 | ); 58 | }); 59 | } 60 | 61 | function drawLandmarks(detections) { 62 | p.noFill(); 63 | p.stroke(161, 95, 251) 64 | p.strokeWeight(2) 65 | 66 | for(let i = 0; i < detections.length; i++){ 67 | const mouth = detections[i].parts.mouth; 68 | const nose = detections[i].parts.nose; 69 | const leftEye = detections[i].parts.leftEye; 70 | const rightEye = detections[i].parts.rightEye; 71 | const rightEyeBrow = detections[i].parts.rightEyeBrow; 72 | const leftEyeBrow = detections[i].parts.leftEyeBrow; 73 | 74 | drawPart(mouth, true); 75 | drawPart(nose, false); 76 | drawPart(leftEye, true); 77 | drawPart(leftEyeBrow, false); 78 | drawPart(rightEye, true); 79 | drawPart(rightEyeBrow, false); 80 | 81 | } 82 | } 83 | 84 | function drawPart(feature, closed) { 85 | p.textSize(9); 86 | p.beginShape(); 87 | for(let i = 0; i < feature.length; i++){ 88 | const x = feature[i]._x 89 | const y = feature[i]._y 90 | p.vertex(x, y) 91 | p.text(`${i}`, x + 5, y - 5); 92 | } 93 | 94 | if(closed === true){ 95 | p.endShape(p.CLOSE); 96 | } else { 97 | p.endShape(); 98 | } 99 | } 100 | 101 | function mouthOpenness(detections) { 102 | if (detections.length === 0) return; 103 | const mouth = detections[0].parts.mouth; 104 | const alignedRect = detections[0].alignedRect; 105 | const headWidth = alignedRect._box._width; 106 | 107 | //13-19 and 14-18 108 | const unnormalizedOpenness = (mouth[19]._y - mouth[13]._y + mouth[18]._y - mouth[14]._y) / 2; 109 | const normalizedOpenness = unnormalizedOpenness / headWidth; 110 | return normalizedOpenness; 111 | } 112 | 113 | function mouthWidth(detections) { 114 | if (detections.length === 0) return; 115 | const mouth = detections[0].parts.mouth; 116 | const alignedRect = detections[0].alignedRect; 117 | const headWidth = alignedRect._box._width; 118 | 119 | //12 - 6 120 | const unnormalizedWidth = Math.abs(mouth[12]._x - mouth[6]._x); 121 | const normalizedWidth = unnormalizedWidth / headWidth; 122 | return normalizedWidth; 123 | } 124 | 125 | p.setup = () => { 126 | p.createCanvas(width, height); 127 | p.background(100, 200, 100); 128 | 129 | p5video = p.createCapture(p.VIDEO); 130 | p5video.size(width, height); 131 | p5video.hide(); 132 | 133 | faceapi = ml5.faceApi(p5video, { 134 | withLandmarks: true, 135 | withDescriptors: false 136 | }, modelReady); 137 | } 138 | 139 | p.draw = () => { 140 | p.image(p5video, 0, 0, p.width, p.height); 141 | 142 | if (detections) { 143 | drawBox(detections); 144 | // drawLandmarks(detections); 145 | const openness = mouthOpenness(detections); 146 | const mwidth = mouthWidth(detections); 147 | const mouthState = openness > 0.03; 148 | handleMouthState(mouthState); 149 | handleMouthWidth(mwidth); 150 | 151 | p.textAlign(p.CENTER); 152 | p.textSize(48); 153 | p.text(mouthState ? "Mouth Open" : "Mouth Closed", p.width / 2, p.height - 0); 154 | } 155 | } 156 | } 157 | 158 | async function setup() { 159 | const myp5 = new p5(p5draw, "main"); 160 | } 161 | 162 | function modelReady() { 163 | console.log("model ready!"); 164 | faceapi.detect(gotResults); 165 | } 166 | 167 | function gotResults(err, results) { 168 | if (err) { 169 | console.log(err); 170 | return; 171 | } 172 | 173 | detections = results; 174 | faceapi.detect(gotResults); 175 | } 176 | 177 | window.addEventListener("DOMContentLoaded", setup); 178 | -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Starter React 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-react", 3 | "version": "0.0.1", 4 | "description": "A small scaffold to get you started using React with Webpack", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack && node server.js", 9 | "watch": "NODE_ENV=development node server.js" 10 | }, 11 | "dependencies": { 12 | "express": "^4.16.4", 13 | "jsx-loader": "^0.13.2", 14 | "p5": "^1.0.0", 15 | "react": "^16.8.6", 16 | "react-dom": "^16.8.6", 17 | "startaudiocontext": "^1.2.1", 18 | "tone": "^13.8.25", 19 | "webpack": "^4.30.0", 20 | "webpack-cli": "^3.3.0", 21 | "webpack-dev-middleware": "^3.7.2", 22 | "webpack-dev-server": "^3.10.3" 23 | }, 24 | "engines": { 25 | "node": "12.x" 26 | }, 27 | "repository": { 28 | "url": "https://glitch.com/edit/#!/starter-react" 29 | }, 30 | "license": "MIT", 31 | "keywords": [ 32 | "node", 33 | "glitch", 34 | "express" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Futura; 3 | } -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // init project 4 | const express = require('express'); 5 | const app = express(); 6 | const port = process.env.PORT || 3000; 7 | 8 | // Special piece for running with webpack dev server 9 | if (process.env.NODE_ENV === "development") { 10 | const webpack = require('webpack'); 11 | const webpackDevMiddleware = require('webpack-dev-middleware'); 12 | const config = require('./webpack.dev.config.js'); 13 | const compiler = webpack(config); 14 | 15 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 16 | // configuration file as a base. 17 | app.use(webpackDevMiddleware(compiler, { 18 | publicPath: config.output.publicPath, 19 | })); 20 | } 21 | 22 | // http://expressjs.com/en/starter/static-files.html 23 | app.use(express.static('public')); 24 | 25 | // http://expressjs.com/en/starter/basic-routing.html 26 | app.get("/", function(request, response) { 27 | response.sendFile(__dirname + '/app/index.html'); 28 | }); 29 | 30 | // listen for requests :) 31 | const listener = app.listen(port, function () { 32 | console.log('Your app is listening on port ' + port); 33 | }); 34 | -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | output: { 8 | path: path.join(__dirname, 'public'), 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.jsx'], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?$/, 18 | loader: 'jsx-loader', 19 | exclude: /node_modules/, 20 | include: path.join(__dirname, 'app'), 21 | }, 22 | ], 23 | }, 24 | }; -------------------------------------------------------------------------------- /lessons/08-video/ml5-app/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'bundle.js', 11 | publicPath: '/', 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.jsx'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.jsx?$/, 20 | loader: 'jsx-loader', 21 | exclude: /node_modules/, 22 | include: path.join(__dirname, 'app'), 23 | }, 24 | ], 25 | }, 26 | devServer: { 27 | contentBase: path.join(__dirname, 'public'), 28 | port: 3000 29 | } 30 | }; -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js 2 | node_modules -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jenn Schiffer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/README.md: -------------------------------------------------------------------------------- 1 | Starter React App on Glitch 2 | =========================== 3 | 4 | This app is a very small scaffold to get you started using React and Webpack. 5 | 6 | It's been copied by @starakaj for your enjoyment. You can find the original at https://glitch.com/~starter-react. 7 | 8 | This project relates to video 2 of 5 in the [React Starter Kit](https://glitch.com/react-starter-kit) video series. 9 | -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/app/app.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const ReactDOM = require("react-dom"); 3 | 4 | /* Import Components */ 5 | const RootComponent = require("./components/RootComponent"); 6 | 7 | ReactDOM.render(, document.getElementById("main")); -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/app/components/ClockFace.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | 3 | function ClockFace(props) { 4 | 5 | const [date, setDate] = React.useState(new Date()); 6 | 7 | // This will be called whenever the component renders, but because we pass an empty 8 | // array as the second argument, it will only be called once, when the component 9 | // first renders. 10 | React.useEffect(() => { 11 | 12 | const timerId = setInterval(() => { 13 | setDate(new Date); 14 | }, 1000); 15 | 16 | // By returning a function from useEffect, we tell React that we'd like this 17 | // function called when the component is unmounted 18 | return () => { clearInterval(timerId) }; 19 | 20 | }, []); 21 | 22 | let prefix = ""; 23 | let postfix = "" 24 | if (props.language === "en") { 25 | prefix = "It is"; 26 | postfix = "o'clock"; 27 | } else if (props.language === "fr") { 28 | prefix = "Il est"; 29 | postfix = "heures"; 30 | } 31 | 32 | return ( 33 |

{prefix} {date.getHours()}:{date.getMinutes()}:{date.getSeconds()} {postfix}

34 | ); 35 | } 36 | 37 | module.exports = ClockFace; -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/app/components/PoemDisplay.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | 3 | module.exports = function(props) { 4 | const [poem, setPoem] = React.useState([]); 5 | 6 | React.useEffect(() => { 7 | fetch("/poem") 8 | .then(result => result.json()) 9 | .then(lines => setPoem(lines)); 10 | }, []); 11 | 12 | const lineElts = poem.map((line, i) =>
  • {line}
  • ); 13 | 14 | return (
      {lineElts}
    ); 15 | } -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/app/components/RootComponent.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const PoemDisplay = require("./PoemDisplay"); 3 | 4 | /* the main page for the index route of this app */ 5 | const RootComponent = function() { 6 | return ( 7 |
    8 |

    Automatic Poem Generator

    9 | 10 | 11 |
    12 | ); 13 | } 14 | 15 | module.exports = RootComponent; -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Starter React 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
    14 | 15 |
    16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-react", 3 | "version": "0.0.1", 4 | "description": "A small scaffold to get you started using React with Webpack", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "poem": "node poem/poem.js", 9 | "start": "webpack && node server.js", 10 | "watch": "NODE_ENV=development node server.js" 11 | }, 12 | "dependencies": { 13 | "express": "^4.16.4", 14 | "jsx-loader": "^0.13.2", 15 | "osmosis": "^1.1.10", 16 | "pos": "^0.4.2", 17 | "react": "^16.8.6", 18 | "react-dom": "^16.8.6", 19 | "rhyme": "0.0.3", 20 | "sbd": "^1.0.17", 21 | "syllable": "^4.0.1", 22 | "webpack": "^4.30.0", 23 | "webpack-cli": "^3.3.0", 24 | "webpack-dev-middleware": "^3.7.2", 25 | "webpack-dev-server": "^3.10.3" 26 | }, 27 | "engines": { 28 | "node": "12.x" 29 | }, 30 | "repository": { 31 | "url": "https://glitch.com/edit/#!/starter-react" 32 | }, 33 | "license": "MIT", 34 | "keywords": [ 35 | "node", 36 | "glitch", 37 | "express" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Futura; 3 | } -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // init project 4 | const express = require('express'); 5 | const { makePoem } = require("./poem/poem"); 6 | const app = express(); 7 | const port = process.env.PORT || 3000; 8 | 9 | // Special piece for running with webpack dev server 10 | if (process.env.NODE_ENV === "development") { 11 | const webpack = require('webpack'); 12 | const webpackDevMiddleware = require('webpack-dev-middleware'); 13 | const config = require('./webpack.dev.config.js'); 14 | const compiler = webpack(config); 15 | 16 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 17 | // configuration file as a base. 18 | app.use(webpackDevMiddleware(compiler, { 19 | publicPath: config.output.publicPath, 20 | })); 21 | } 22 | 23 | // http://expressjs.com/en/starter/static-files.html 24 | app.use(express.static('public')); 25 | 26 | // http://expressjs.com/en/starter/basic-routing.html 27 | app.get("/", function(request, response) { 28 | response.sendFile(__dirname + '/app/index.html'); 29 | }); 30 | 31 | app.get("/poem", async function(_, response) { 32 | const lines = await makePoem(); 33 | response.json(lines); 34 | }); 35 | 36 | // listen for requests :) 37 | const listener = app.listen(port, function () { 38 | console.log('Your app is listening on port ' + port); 39 | }); 40 | -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.jsx', 7 | output: { 8 | path: path.join(__dirname, 'public'), 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.jsx'], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?$/, 18 | loader: 'jsx-loader', 19 | exclude: /node_modules/, 20 | include: path.join(__dirname, 'app'), 21 | }, 22 | ], 23 | }, 24 | }; -------------------------------------------------------------------------------- /lessons/09-language/poem-docu/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.jsx', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'bundle.js', 11 | publicPath: '/', 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.jsx'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.jsx?$/, 20 | loader: 'jsx-loader', 21 | exclude: /node_modules/, 22 | include: path.join(__dirname, 'app'), 23 | }, 24 | ], 25 | }, 26 | devServer: { 27 | contentBase: path.join(__dirname, 'public'), 28 | port: 3000 29 | } 30 | }; -------------------------------------------------------------------------------- /lessons/10-learning/commander-test/myscript.js: -------------------------------------------------------------------------------- 1 | const { program } = require("commander"); 2 | 3 | program 4 | .option("-r", "Raw pasta", false) 5 | .option("-p, --pasta ", "What kind of pasta you want", "spaghetti") 6 | .option("-s, ", "What kind of sauce you want", "tomato") 7 | .parse(process.argv); 8 | 9 | console.log(program.R); 10 | console.log(program.pasta); 11 | console.log(program.S); 12 | -------------------------------------------------------------------------------- /lessons/10-learning/commander-test/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "commander-test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "commander": { 8 | "version": "5.0.0", 9 | "resolved": "https://registry.npmjs.org/commander/-/commander-5.0.0.tgz", 10 | "integrity": "sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lessons/10-learning/commander-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "commander-test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "commander": "^5.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/10-learning/img/learning/learning.001.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/10-learning/img/learning/learning.001.jpeg -------------------------------------------------------------------------------- /lessons/10-learning/img/learning/learning.002.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/10-learning/img/learning/learning.002.jpeg -------------------------------------------------------------------------------- /lessons/10-learning/img/learning/learning.003.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/10-learning/img/learning/learning.003.jpeg -------------------------------------------------------------------------------- /lessons/10-learning/img/learning/learning.004.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/10-learning/img/learning/learning.004.jpeg -------------------------------------------------------------------------------- /lessons/10-learning/img/learning/learning.005.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starakaj/programming-is-possible/a489eac142b49b6c9054d59e5c2502a9e976956f/lessons/10-learning/img/learning/learning.005.jpeg -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js 2 | node_modules 3 | data -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jenn Schiffer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/README.md: -------------------------------------------------------------------------------- 1 | Teachable Machine Scaffold 2 | =========================== 3 | 4 | This app is a very small scaffold to get you started using Teachable Machine. It will require an internet connection to work, since it loads ml5 as well as TensorFlow from a CDN. 5 | 6 | ## Basic Usage 7 | 8 | ``` 9 | npm run watch 10 | ``` 11 | 12 | will start a webpack server at localhost:3000. From the browser you'll be able to see a simple example that recognizes "heart hands," or when you make your hands look like this ![Heart Hands](https://image.shutterstock.com/image-photo/woman-making-heart-her-hands-600w-1211985307.jpg) 13 | 14 | If you want to see the sound recognition or drawing recognition examples, you'll need to comment out the appropriate line in `app/app.js` to change which `setup` function is loaded. 15 | 16 | ## Running the downloader 17 | 18 | This starter includes a script that you can use to download images off of Image Net. To download 100 random images to a directory called `data/random` you could run 19 | 20 | ```sh 21 | npm run images -- -o data/random 22 | ``` 23 | 24 | If you wanted instead to get 100 images with a particular wnid, then you'd execute something like this: 25 | 26 | ```sh 27 | npm run images -- -o data/red-panda --wnid n02509815 --randomize 28 | ``` 29 | 30 | Which will download 100 images of a red panda. The `--randomize` flag tells the downloader whether to download the first 100 images, or to scramble the list of images first. You can also use the `--count` flag to limit or increase the number of images downloaded. Note that many links in Image Net are broken, and you may see errors in the console while downloading. 31 | 32 | ## Attribution 33 | A long time ago this was a starter on Glitch using React and Webpack. It was copied by @starakaj, and then React has been removed You can find the original at https://glitch.com/~starter-react. 34 | 35 | This project relates to video 2 of 5 in the [React Starter Kit](https://glitch.com/react-starter-kit) video series. 36 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/app/app.js: -------------------------------------------------------------------------------- 1 | // Require different files to change behavior 2 | // const setup = require("./imageDetector/imageDetectorApp"); 3 | // const setup = require("./soundDetector/soundDetectorApp"); 4 | // const setup = require("./drawDetector/drawDetectorApp"); 5 | const setup = require("./heartHands/heartHandsApp"); 6 | 7 | // Calls the setup function when the page is loaded 8 | window.addEventListener("DOMContentLoaded", setup); 9 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/app/drawDetector/drawDetectorApp.js: -------------------------------------------------------------------------------- 1 | const p5 = require("p5"); 2 | 3 | // Model URL 4 | // If you make your own model, this is where you'd link to it. This is a model 5 | // that I trained on drawings of circles and squares. It should be able to distinguish 6 | // those two at least 7 | const imageModelURL = 'https://teachablemachine.withgoogle.com/models/_W_IEN88s/'; 8 | 9 | // Whether or not you want to flip the video horizontally. If you trained your model 10 | // using your webcam, then you'll want to enable this 11 | const flipVideo = true; 12 | const width = 320; 13 | const height = 260; 14 | 15 | const p5draw = (p) => { 16 | 17 | let classifier; 18 | let drawingCanvas; 19 | let label = ""; 20 | 21 | p.setup = () => { 22 | p.createCanvas(width, height); 23 | p.background(255); 24 | 25 | // We want the drawing to persist between calls to draw, 26 | // so we make a graphics context into which we can draw 27 | drawingCanvas = p.createGraphics(width, height); 28 | drawingCanvas.background(235); 29 | 30 | classifier = ml5.imageClassifier(imageModelURL + 'model.json', classifyImage); 31 | }; 32 | 33 | p.draw = () => { 34 | // Draw a small black circle under the mouse 35 | if (p.mouseIsPressed) { 36 | drawingCanvas.strokeWeight(0); 37 | drawingCanvas.fill(0); 38 | drawingCanvas.ellipse(p.mouseX, p.mouseY, 5, 5); 39 | } 40 | 41 | // To draw what we see, first erase 42 | p.background(235); 43 | 44 | // Draw the drawing canvas 45 | p.image(drawingCanvas, 0, 0, width, height); 46 | 47 | // Draw the label 48 | let textToDraw = label === "" ? "Draw! Space to clear, s to save." : label; 49 | p.fill(0); 50 | p.textSize(16); 51 | p.textAlign(p.CENTER); 52 | p.text(textToDraw, width / 2, height - 4); 53 | }; 54 | 55 | p.keyPressed = () => { 56 | if (p.key === " ") { 57 | label = ""; 58 | drawingCanvas.background(235); 59 | } else if (p.key === "s") { 60 | p.saveCanvas(drawingCanvas); 61 | } 62 | } 63 | 64 | // Get a prediction for the current video frame 65 | function classifyImage() { 66 | classifier.classify(drawingCanvas, gotResult); 67 | } 68 | 69 | function gotResult(error, results) { 70 | if (error) { 71 | console.error(error); 72 | return; 73 | } 74 | 75 | // results is an array, sorted by confidence. Each 76 | // result will look like { label: "category label" confidence: 0.453 } 77 | // or something like this 78 | if (results[0].confidence > 0.75) 79 | label = results[0].label; 80 | classifyImage(); 81 | } 82 | } 83 | 84 | module.exports = function setup() { 85 | const myp5 = new p5(p5draw, "main"); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/app/heartHands/heart.js: -------------------------------------------------------------------------------- 1 | module.exports = class Heart { 2 | constructor(x, y, vx, vy, a, va) { 3 | this.x = x; 4 | this.y = y; 5 | this.vx = vx; 6 | this.vy = vy; 7 | this.a = a; 8 | this.va = va; 9 | this._scale = 1.0; 10 | } 11 | 12 | set scale(s) { 13 | this._scale = s; 14 | } 15 | 16 | update() { 17 | this.x += this.vx; 18 | this.y += this.vy; 19 | this.a += this.va; 20 | this.vy += 1.0; 21 | } 22 | 23 | draw(p) { 24 | p.push(); 25 | p.noFill(); 26 | p.stroke(255, 135, 231); 27 | 28 | const n = 11; 29 | const u1 = 5; 30 | const v1 = -6; 31 | const u2 = 14; 32 | const v2 = 5; 33 | const v3 = 11; 34 | 35 | p.translate(this.x, this.y); 36 | p.rotate(this.a); 37 | p.scale(this._scale); 38 | p.strokeWeight(3 / this._scale); 39 | p.bezier(0, 0, 0 + u1/n, 0 + v1/n, 0 + u2/n, 0 + v2/n, 0, 0 + v3/n); 40 | p.bezier(0, 0, 0 - u1/n, 0 + v1/n, 0 - u2/n, 0 + v2/n, 0, 0 + v3/n); 41 | 42 | p.pop(); 43 | } 44 | 45 | isOffscreen(p) { 46 | return this.x < 0 - this._scale || 47 | this.x > p.width + this._scale || 48 | this.y < 0 - this._scale || 49 | this.y > p.height + this._scale; 50 | } 51 | } -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/app/heartHands/heartHandsApp.js: -------------------------------------------------------------------------------- 1 | const p5 = require("p5"); 2 | const Heart = require("./heart"); 3 | 4 | // Model URL 5 | // If you make your own model, this is where you'd link to it. This is a model 6 | // that I trained on making "heart hands", like this 7 | // https://image.shutterstock.com/image-photo/woman-making-heart-her-hands-600w-1211985307.jpg 8 | const imageModelURL = 'https://teachablemachine.withgoogle.com/models/sltRChS8U/'; 9 | 10 | // Whether or not you want to flip the video horizontally. If you trained your model 11 | // using your webcam, then you'll want to enable this 12 | const flipVideo = true; 13 | const width = 320; 14 | const height = 260; 15 | 16 | const p5draw = (p) => { 17 | 18 | let p5video; 19 | let offscreenGraphics; 20 | let classifier; 21 | let label = ""; 22 | let hearts = []; 23 | 24 | p.setup = () => { 25 | p.createCanvas(width, height); 26 | p.background(255); 27 | 28 | p5video = p.createCapture(p.VIDEO); 29 | p5video.size(width, height); 30 | p5video.hide(); 31 | 32 | // We'll use this offscreen canvas to store the video, in case we 33 | // want to transform it before classifying it 34 | offscreenGraphics = p.createGraphics(width, height); 35 | 36 | classifier = ml5.imageClassifier(imageModelURL + "model.json", classifyVideo); 37 | } 38 | 39 | p.draw = () => { 40 | // This draws the video with X and Y flipped 41 | offscreenGraphics.push(); 42 | if (flipVideo) { 43 | offscreenGraphics.translate(width, 0); 44 | offscreenGraphics.scale(-1, 1); 45 | } 46 | offscreenGraphics.image(p5video, 0, 0, width, height); 47 | offscreenGraphics.pop(); 48 | 49 | p.image(offscreenGraphics, 0, 0, p.width, p.height); 50 | 51 | // Draw the label 52 | p.fill(255); 53 | p.textSize(16); 54 | p.textAlign(p.CENTER); 55 | p.text(label, p.width / 2, p.height - 4); 56 | 57 | if (label.toLowerCase().startsWith("heart")) { 58 | const h = new Heart( 59 | p.width / 2.0, 60 | p.height / 2.0, 61 | p.map(Math.random(), 0, 1, -1, 1) * 10, 62 | p.map(Math.random(), 0, 1, -1, 1) * 10, 63 | p.map(Math.random(), 0, 1, 0, 2) * Math.PI, 64 | p.map(Math.random(), 0, 1, -1, 1) * Math.PI * 0.05 65 | ); 66 | h.scale = p.map(Math.random(), 0, 1, 10, 50); 67 | hearts.push(h); 68 | } 69 | 70 | hearts.forEach(heart => heart.draw(p)); 71 | hearts.forEach(heart => heart.update()); 72 | hearts = hearts.filter(heart => !heart.isOffscreen(p)); 73 | } 74 | 75 | // Get a prediction for the current video frame 76 | function classifyVideo() { 77 | classifier.classify(offscreenGraphics, gotResult); 78 | } 79 | 80 | function gotResult(error, results) { 81 | if (error) { 82 | console.error(error); 83 | return; 84 | } 85 | 86 | // results is an array, sorted by confidence. Each 87 | // result will look like { label: "category label" confidence: 0.453 } 88 | // or something like this 89 | label = results[0].label; 90 | classifyVideo(); 91 | } 92 | } 93 | 94 | module.exports = function setup() { 95 | const myp5 = new p5(p5draw, "main"); 96 | } 97 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/app/imageDetector/imageDetectorApp.js: -------------------------------------------------------------------------------- 1 | const p5 = require("p5"); 2 | 3 | // Model URL 4 | // If you make your own model, this is where you'd link to it. This is a model 5 | // that I trained on making "heart hands", like this 6 | // https://image.shutterstock.com/image-photo/woman-making-heart-her-hands-600w-1211985307.jpg 7 | const imageModelURL = 'https://teachablemachine.withgoogle.com/models/sltRChS8U/'; 8 | 9 | // Whether or not you want to flip the video horizontally. If you trained your model 10 | // using your webcam, then you'll want to enable this 11 | const flipVideo = true; 12 | const width = 320; 13 | const height = 260; 14 | 15 | const p5draw = (p) => { 16 | 17 | let classifier; 18 | let p5video; 19 | let offscreenGraphics; 20 | let label = ""; 21 | 22 | p.setup = () => { 23 | p.createCanvas(width, height); 24 | p.background(255); 25 | 26 | p5video = p.createCapture(p.VIDEO); 27 | p5video.size(width, height); 28 | p5video.hide(); 29 | 30 | // We'll use this offscreen canvas to store the video, in case we 31 | // want to transform it before classifying it 32 | offscreenGraphics = p.createGraphics(width, height); 33 | 34 | classifier = ml5.imageClassifier(imageModelURL + 'model.json', classifyVideo); 35 | } 36 | 37 | p.draw = () => { 38 | // This draws the video with X and Y flipped 39 | offscreenGraphics.push(); 40 | if (flipVideo) { 41 | offscreenGraphics.translate(width, 0); 42 | offscreenGraphics.scale(-1, 1); 43 | } 44 | offscreenGraphics.image(p5video, 0, 0, width, height); 45 | offscreenGraphics.pop(); 46 | 47 | p.image(offscreenGraphics, 0, 0, p.width, p.height); 48 | 49 | // Draw the label 50 | p.fill(255); 51 | p.textSize(16); 52 | p.textAlign(p.CENTER); 53 | p.text(label, width / 2, height - 4); 54 | } 55 | 56 | // Get a prediction for the current video frame 57 | function classifyVideo() { 58 | classifier.classify(offscreenGraphics, gotResult); 59 | } 60 | 61 | function gotResult(error, results) { 62 | if (error) { 63 | console.error(error); 64 | return; 65 | } 66 | 67 | // results is an array, sorted by confidence. Each 68 | // result will look like { label: "category label" confidence: 0.453 } 69 | // or something like this 70 | label = results[0].label; 71 | classifyVideo(); 72 | } 73 | } 74 | 75 | module.exports = function setup() { 76 | const myp5 = new p5(p5draw, "main"); 77 | } 78 | 79 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Teachable Machine Starter 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 | 17 |
    18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/app/soundDetector/soundDetectorApp.js: -------------------------------------------------------------------------------- 1 | const p5 = require("p5"); // Just so it's easy to draw 2 | 3 | // Model URL 4 | // If you make your own model, this is where you'd link to it. This is a model 5 | // that I trained on making on saying the word "beep". Hopefully 6 | // if you say the word "beep" in isolation it will detect it. Unfortunately 7 | // it's only trained on my voice. 8 | const soundModelURL = 'https://teachablemachine.withgoogle.com/models/Q-n8u5SXp/'; 9 | 10 | // These are the options that you can pass to your sound classifier when creating 11 | // it. Unless you pass "invokeCallbackOnNoiseAndUnknown: true", the callback 12 | // will only trigger when one of the non-noise categories is recognized. 13 | const soundClassifierOptions = { 14 | includeSpectrogram: true, // in case listen should return result.spectrogram 15 | probabilityThreshold: 0.75, 16 | invokeCallbackOnNoiseAndUnknown: true, 17 | overlapFactor: 0.50 // probably want between 0.5 and 0.75. 18 | } 19 | 20 | const width = 320; 21 | const height = 260; 22 | 23 | const p5draw = (p) => { 24 | 25 | let classifier; 26 | let label = "listening..."; 27 | 28 | p.setup = () => { 29 | p.createCanvas(width, height); 30 | p.background(255); 31 | 32 | classifier = ml5.soundClassifier(soundModelURL + 'model.json', soundClassifierOptions, audioClassifierReady); 33 | } 34 | 35 | p.draw = () => { 36 | p.background(0); 37 | 38 | // Draw the label 39 | p.fill(255); 40 | p.textSize(16); 41 | p.textAlign(p.CENTER); 42 | p.text(label, width / 2, height - 4); 43 | } 44 | 45 | // Unlike the video classifier, this classifier will run continuously, 46 | // calling gotResult again and again 47 | function audioClassifierReady() { 48 | classifier.classify(gotResult); 49 | } 50 | 51 | function gotResult(error, results) { 52 | if (error) { 53 | console.error(error); 54 | return; 55 | } 56 | 57 | // results is an array, sorted by confidence. Each 58 | // result will look like { label: "category label" confidence: 0.453 } 59 | // or something like this 60 | label = results[0].label; 61 | } 62 | } 63 | 64 | module.exports = function setup() { 65 | const myp5 = new p5(p5draw, "main"); 66 | } 67 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-react", 3 | "version": "0.0.1", 4 | "description": "A small scaffold to get you started using React with Webpack", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "images": "node scripts/imageDownloader.js", 9 | "start": "webpack && node server.js", 10 | "watch": "NODE_ENV=development node server.js" 11 | }, 12 | "dependencies": { 13 | "commander": "^5.0.0", 14 | "express": "^4.16.4", 15 | "jsx-loader": "^0.13.2", 16 | "mkdirp": "^1.0.4", 17 | "node-fetch": "^2.6.0", 18 | "osmosis": "^1.1.10", 19 | "p5": "^1.0.0", 20 | "react": "^16.8.6", 21 | "react-dom": "^16.8.6", 22 | "webpack": "^4.30.0", 23 | "webpack-cli": "^3.3.0", 24 | "webpack-dev-middleware": "^3.7.2", 25 | "webpack-dev-server": "^3.10.3" 26 | }, 27 | "engines": { 28 | "node": "12.x" 29 | }, 30 | "repository": { 31 | "url": "https://glitch.com/edit/#!/starter-react" 32 | }, 33 | "license": "MIT", 34 | "keywords": [ 35 | "node", 36 | "glitch", 37 | "express" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Futura; 3 | } -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/scripts/imageDownloader.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const osmosis = require("osmosis"); 6 | const fetch = require("node-fetch"); 7 | const mkdirp = require("mkdirp"); 8 | const { program } = require("commander"); 9 | 10 | program 11 | .option("-d, --dry", "Dry run--print what would happen but don't do it") 12 | .option('-r, --randomize', 'Randomize the order of downloaded images') 13 | .option('--count ', 'number of images to download', 100) 14 | .option('-o, --output ', 'Directory to write to', process.cwd()) 15 | .option("--wnid ", "Source Word Net ID. Leave blank for random images") 16 | .parse(process.argv); 17 | 18 | const stopAt = program.count; 19 | const randomize = program.randomize; 20 | const wnid = program.wnid; 21 | const destPath = path.normalize(program.output); 22 | 23 | const sysnetListUrl = "http://www.image-net.org/api/text/imagenet.synset.obtain_synset_list"; 24 | const imagenetRoot = "http://image-net.org/api/text/imagenet.synset.geturls?wnid="; 25 | 26 | if (!!wnid) { 27 | const sourceUrl = imagenetRoot + wnid; 28 | console.log(`Downloading up to ${stopAt} images from ${sourceUrl} to ${destPath} in ${randomize ? "random order" : "nonrandom order"}`); 29 | } else { 30 | console.log(`Downloading up to ${stopAt} Image Net images at random to ${destPath}`); 31 | } 32 | 33 | if (program.dry) process.exit(0); 34 | 35 | // Try to make the output directory 36 | mkdirp.sync(destPath); 37 | 38 | if (!!wnid) { 39 | // download images from a particular wnid link 40 | (async function() { 41 | const pageUrl = imagenetRoot + wnid; 42 | const pageLinks = await getLinksFromImageNetPage(pageUrl, stopAt, randomize); 43 | await Promise.all(pageLinks.map(async link => { 44 | const pathname = (new URL(link)).pathname; 45 | const basename = path.basename(pathname); 46 | const filename = path.join(destPath, basename); 47 | try { 48 | await download(link, filename); 49 | } catch (e) { 50 | console.log("Could not download image from " + link); 51 | } 52 | })) 53 | })(); 54 | } else { 55 | // download images at random 56 | (async function() { 57 | const randomWNIDs = await getRandomImageNetLinks(stopAt); 58 | const randomLinks = randomWNIDs.map(wnid => imagenetRoot + wnid); 59 | await Promise.all(randomLinks.map(async link => { 60 | const pageLinks = await getLinksFromImageNetPage(link, 1, true); 61 | const pathname = (new URL(pageLinks[0])).pathname; 62 | const basename = path.basename(pathname); 63 | const filename = path.join(destPath, basename); 64 | try { 65 | await download(pageLinks[0], filename); 66 | } catch (e) { 67 | console.log("Could not download image from " + pageLinks[0]); 68 | } 69 | })); 70 | })(); 71 | } 72 | 73 | // Shuffle an array in place 74 | function scramble(array) { 75 | for (let i = 0; i < array.length; i++) { 76 | const randomIndex = Math.floor(Math.random() * array.length); 77 | const tmp = array[randomIndex]; 78 | array[randomIndex] = array[i]; 79 | array[i] = tmp; 80 | } 81 | } 82 | 83 | // Return an array of random Image Net links 84 | async function getRandomImageNetLinks(count) { 85 | return getLinksFromImageNetPage(sysnetListUrl, count, true); 86 | } 87 | 88 | // Return an array of Image Net links from a page 89 | async function getLinksFromImageNetPage(pageUrl, count, doScramble) { 90 | return new Promise((resolve, reject) => { 91 | let nidsText; 92 | osmosis.get(pageUrl) 93 | .find("body") 94 | .set("nids") 95 | .data(item => nidsText = item.nids) 96 | .done(() => { 97 | if (nidsText) { 98 | let links = nidsText.split("\n"); 99 | if (doScramble) scramble(links); 100 | links = links.slice(0, count); 101 | links = links.map(link => link.trim()); 102 | resolve(links); 103 | } 104 | }) 105 | .error((e) => reject(e)); 106 | }); 107 | } 108 | 109 | // Download the file at a uri to a location 110 | async function download(uri, filename) { 111 | return new Promise((resolve) => { 112 | fetch(uri).then(res => { 113 | if (res.ok) { 114 | const dest = fs.createWriteStream(filename); 115 | res.body.pipe(dest).on("close", resolve); 116 | } 117 | }); 118 | }); 119 | }; 120 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // init project 4 | const express = require('express'); 5 | const app = express(); 6 | const port = process.env.PORT || 3000; 7 | 8 | // Special piece for running with webpack dev server 9 | if (process.env.NODE_ENV === "development") { 10 | const webpack = require('webpack'); 11 | const webpackDevMiddleware = require('webpack-dev-middleware'); 12 | const config = require('./webpack.dev.config.js'); 13 | const compiler = webpack(config); 14 | 15 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 16 | // configuration file as a base. 17 | app.use(webpackDevMiddleware(compiler, { 18 | publicPath: config.output.publicPath, 19 | })); 20 | } 21 | 22 | // http://expressjs.com/en/starter/static-files.html 23 | app.use(express.static('public')); 24 | 25 | // http://expressjs.com/en/starter/basic-routing.html 26 | app.get("/", function(request, response) { 27 | response.sendFile(__dirname + '/app/index.html'); 28 | }); 29 | 30 | // listen for requests :) 31 | const listener = app.listen(port, function () { 32 | console.log('Your app is listening on port ' + port); 33 | }); 34 | -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | output: { 8 | path: path.join(__dirname, 'public'), 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.jsx'], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?$/, 18 | loader: 'jsx-loader', 19 | exclude: /node_modules/, 20 | include: path.join(__dirname, 'app'), 21 | }, 22 | ], 23 | }, 24 | }; -------------------------------------------------------------------------------- /lessons/10-learning/teachable-machine-app/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'bundle.js', 11 | publicPath: '/', 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.jsx'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.jsx?$/, 20 | loader: 'jsx-loader', 21 | exclude: /node_modules/, 22 | include: path.join(__dirname, 'app'), 23 | }, 24 | ], 25 | }, 26 | devServer: { 27 | contentBase: path.join(__dirname, 'public'), 28 | port: 3000 29 | } 30 | }; -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js 2 | node_modules -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jenn Schiffer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/README.md: -------------------------------------------------------------------------------- 1 | Express Game Starter 2 | =========================== 3 | 4 | This app is a very small scaffold to get started making a multiplayer game using p5, express and websockets. 5 | 6 | It's been copied by @starakaj for your enjoyment, and then React has been removed You can find the original at https://glitch.com/~starter-react. 7 | 8 | This project relates to video 2 of 5 in the [React Starter Kit](https://glitch.com/react-starter-kit) video series. 9 | -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/app/app.js: -------------------------------------------------------------------------------- 1 | const p5 = require("p5"); 2 | const Game = require("./game"); 3 | const GameClient = require("./gameClient"); 4 | 5 | const width = 400; 6 | const height = 400; 7 | const columns = 10; 8 | const rows = 10; 9 | const cellWidth = width / columns; 10 | const cellHeight = height / rows; 11 | 12 | const sketch = (p) => { 13 | 14 | let game = new Game(columns, rows); 15 | let gameClient = new GameClient(); 16 | game.on("playerMoved", (player) => gameClient.sendPlayer(player)); 17 | gameClient.on("playersUpdate", (players) => game.updatePlayers(players)); 18 | gameClient.on("connected", () => gameClient.sendPlayer(game.ownedPlayer)); 19 | 20 | p.setup = () => { 21 | p.createCanvas(400, 400); 22 | } 23 | 24 | p.draw = () => { 25 | // Make the background almost white 26 | p.background(240); 27 | 28 | // Draw a nice grid for the background 29 | p.strokeWeight(0.5); 30 | p.stroke(15); 31 | for (let x = 1; x < columns; x++) { 32 | p.line(x * cellWidth, 0, x * cellWidth, height); 33 | } 34 | for (let y = 1; y < rows; y++) { 35 | p.line(0, y * cellHeight, width, y * cellHeight); 36 | } 37 | 38 | // Draw the game 39 | game.draw(p, cellWidth, cellHeight); 40 | } 41 | 42 | p.keyPressed = () => { 43 | game.handleInput(p.key); 44 | } 45 | } 46 | 47 | const myp5 = new p5(sketch, "main"); 48 | -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/app/game.js: -------------------------------------------------------------------------------- 1 | const { EventEmitter } = require("events"); 2 | const { clamp } = require("./util"); 3 | const { v4: uuidv4 } = require("uuid"); 4 | 5 | module.exports = class Game extends EventEmitter { 6 | constructor(columns, rows) { 7 | super(); 8 | this._columns = columns; 9 | this._rows = rows; 10 | 11 | // Position the player in the center of the board 12 | this._player = this._makePlayer( 13 | Math.floor(this._columns / 2), 14 | Math.floor(this._rows / 2), 15 | this._makeRandomHue() 16 | ) 17 | 18 | this._players = { 19 | [this._player.id]: this._player 20 | }; 21 | } 22 | 23 | // Return an HSBA color (Hue, Saturation, Brighness, Alpha) 24 | // with a random hue 25 | _makeRandomHue() { 26 | return [ 27 | Math.random() * 255, 28 | 255, 29 | 255, 30 | 255 31 | ]; 32 | } 33 | 34 | // Make a new player object out of a position and a color 35 | _makePlayer(px, py, color) { 36 | return { 37 | id: uuidv4(), 38 | x: px, 39 | y: py, 40 | color 41 | }; 42 | } 43 | 44 | // Accessor for the player that this game owns, accessed like "game.ownedPlayer" 45 | get ownedPlayer() { 46 | return this._player; 47 | } 48 | 49 | // Draw each player as a square at the appropriate position 50 | draw(p, cellWidth, cellHeight) { 51 | p.push(); 52 | p.strokeWeight(0); 53 | p.colorMode(p.HSB); 54 | Object.values(this._players).forEach(player => { 55 | p.fill(player.color); 56 | p.rect( 57 | player.x * cellWidth, 58 | player.y * cellHeight, 59 | cellWidth, 60 | cellHeight 61 | ); 62 | }); 63 | p.pop(); 64 | } 65 | 66 | _movePlayer(dx, dy) { 67 | this._player.x = clamp(this._player.x + dx, 0, this._columns - 1); 68 | this._player.y = clamp(this._player.y + dy, 0, this._rows - 1); 69 | 70 | this.emit("playerMoved", this._player); 71 | } 72 | 73 | handleInput(key) { 74 | if (key === "ArrowLeft") { 75 | this._movePlayer(-1, 0); 76 | } else if (key === "ArrowRight") { 77 | this._movePlayer(1, 0); 78 | } else if (key === "ArrowUp") { 79 | this._movePlayer(0, -1); 80 | } else if (key === "ArrowDown") { 81 | this._movePlayer(0, 1); 82 | } 83 | } 84 | 85 | updatePlayers(players) { 86 | const ourPlayer = { 87 | [this._player.id]: this._player 88 | }; 89 | 90 | // Make sure that our player stays in there, no matter what 91 | this._players = Object.assign(players, ourPlayer); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/app/gameClient.js: -------------------------------------------------------------------------------- 1 | const { EventEmitter } = require("events"); 2 | 3 | module.exports = class GameClient extends EventEmitter { 4 | constructor() { 5 | super(); 6 | const pageUrl = new URL(window.location); 7 | pageUrl.protocol = "ws"; 8 | this._websocket = new WebSocket(pageUrl.toString()); 9 | 10 | this._websocket.onopen = () => { 11 | this.emit("connected"); 12 | }; 13 | 14 | this._websocket.onmessage = (event) => { 15 | const players = JSON.parse(event.data); 16 | this.emit("playersUpdate", players); 17 | }; 18 | } 19 | 20 | sendPlayer(player) { 21 | this._websocket.send( 22 | JSON.stringify(player) 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MMORPG Starter 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
    14 | 15 |
    16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/app/util.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clamp: (x, min, max) => { 3 | if (x < min) return min; 4 | if (x > max) return max; 5 | return x; 6 | } 7 | } -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-react", 3 | "version": "0.0.1", 4 | "description": "A small scaffold to get you started using React with Webpack", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack && node server.js", 9 | "watch": "NODE_ENV=development node server.js" 10 | }, 11 | "dependencies": { 12 | "express": "^4.16.4", 13 | "jsx-loader": "^0.13.2", 14 | "p5": "^1.0.0", 15 | "react": "^16.8.6", 16 | "react-dom": "^16.8.6", 17 | "uuid": "^7.0.3", 18 | "webpack": "^4.30.0", 19 | "webpack-cli": "^3.3.0", 20 | "webpack-dev-middleware": "^3.7.2", 21 | "webpack-dev-server": "^3.10.3", 22 | "ws": "^7.2.3" 23 | }, 24 | "engines": { 25 | "node": "12.x" 26 | }, 27 | "repository": { 28 | "url": "https://glitch.com/edit/#!/starter-react" 29 | }, 30 | "license": "MIT", 31 | "keywords": [ 32 | "node", 33 | "glitch", 34 | "express" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Futura; 3 | } -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // init project 4 | const express = require('express'); 5 | const app = express(); 6 | const port = process.env.PORT || 3000; 7 | const WebSocket = require("ws"); 8 | const { v4: uuidv4 } = require("uuid"); 9 | 10 | // Special piece for running with webpack dev server 11 | if (process.env.NODE_ENV === "development") { 12 | const webpack = require('webpack'); 13 | const webpackDevMiddleware = require('webpack-dev-middleware'); 14 | const config = require('./webpack.dev.config.js'); 15 | const compiler = webpack(config); 16 | 17 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 18 | // configuration file as a base. 19 | app.use(webpackDevMiddleware(compiler, { 20 | publicPath: config.output.publicPath, 21 | })); 22 | } 23 | 24 | // http://expressjs.com/en/starter/static-files.html 25 | app.use(express.static('public')); 26 | 27 | // http://expressjs.com/en/starter/basic-routing.html 28 | app.get("/", function(request, response) { 29 | response.sendFile(__dirname + '/app/index.html'); 30 | }); 31 | 32 | // listen for requests :) 33 | const listener = app.listen(port, function () { 34 | console.log('Your app is listening on port ' + port); 35 | }); 36 | 37 | // Space to store players, by player id 38 | const players = {}; 39 | 40 | // Map from a connection id, to the player id that the connection owns 41 | const playersByConnectionId = {}; 42 | 43 | // Start a web socket server 44 | const wsServer = new WebSocket.Server({ server: listener }); 45 | 46 | function broadcastPlayers() { 47 | wsServer.clients.forEach(client => { 48 | client.send( 49 | JSON.stringify(players) 50 | ); 51 | }); 52 | } 53 | 54 | class Game { 55 | constructor(leftPlayer, rightPlayer) { 56 | this._leftPlayer = leftPlayer; 57 | this._rightPlayer = rightPlayer; 58 | } 59 | } 60 | 61 | // map from WSUID -> Game 62 | const activeGames = {}; 63 | 64 | const waitingPlayer = null; 65 | 66 | // Handle new connections 67 | wsServer.on("connection", (ws) => { 68 | 69 | // Generate a new UID for this websocket 70 | const wsid = uuidv4(); 71 | 72 | if (waitingPlayer === null) { 73 | waitingPlayer = wsid; 74 | ws.send("staus", {message: "Waiting for another player to join"}); 75 | } else { 76 | const game = new Game(waitingPlayer, wsid); 77 | activeGames[waitingPlayer] = game; 78 | activeGames[wsid] = game; 79 | } 80 | 81 | // Update players whenever a new move gets made 82 | ws.on("message", (data) => { 83 | const game = activeGames[wsid]; 84 | const updateObject = JSON.parse(data); 85 | 86 | if (updateObject.type === "move") { 87 | game.setMove(wsid, updateObject.move); 88 | } 89 | 90 | if (game.leftMove !== null && game.rightMove !== null) { 91 | // broadcast the new state of the game to each player 92 | } 93 | }); 94 | 95 | // Clean up when the player disconnects 96 | ws.on("close", () => { 97 | const playerid = playersByConnectionId[wsid]; 98 | if (playerid) delete players[playerid]; 99 | delete playersByConnectionId[wsid]; 100 | broadcastPlayers(); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | output: { 8 | path: path.join(__dirname, 'public'), 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.jsx'], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?$/, 18 | loader: 'jsx-loader', 19 | exclude: /node_modules/, 20 | include: path.join(__dirname, 'app'), 21 | }, 22 | ], 23 | }, 24 | }; -------------------------------------------------------------------------------- /lessons/11-advanced/game-app/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.js', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'bundle.js', 11 | publicPath: '/', 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.jsx'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.jsx?$/, 20 | loader: 'jsx-loader', 21 | exclude: /node_modules/, 22 | include: path.join(__dirname, 'app'), 23 | }, 24 | ], 25 | }, 26 | devServer: { 27 | contentBase: path.join(__dirname, 'public'), 28 | port: 3000 29 | } 30 | }; -------------------------------------------------------------------------------- /lessons/13-gesture/13-gesture.md: -------------------------------------------------------------------------------- 1 | # Gesture Recognition 2 | 3 | ## Authors 4 | Sam Tarakajian for NYU IDM 5 | 6 | DM-GY 6063 7 | 8 | @starakaj 9 | 10 | ## Essential Questions 11 | - What are the different ways of encoding gestural information for a computer? 12 | - How can we extract useful data from that information? 13 | - What kinds of systems can we drive with that data? 14 | 15 | ## Introduction 16 | In addition to verbal communication, people also have a highly developed language of gestural communication. These gestures can be deliberate forms of person-to-person communication, like waving to say hello, or pointing to indicate a specific object in the distance. They can also communicate between people and objects, for example the gestures by which a violinist communicates with their instuments. Finally, human gestures might communicate something about their environment, in the sense that the swaying of subway passengers tells us something about the motion of the subway car. 17 | 18 | Many computer libraries are designed for working with gestural information. In order to drive computer systems with a gesture, it must first be captured, and then analyzed to retrieve useful information. Cameras, depth sensors, accelerometers and position trackers are all used for gesture capture. Finally, there are several statistical and machine-learning tools that can pull useful information from captured gesture data. 19 | 20 | In this class, we'll look at systems for data capture including webcam, depth camera, and Leap Motion. Next, we'll see how to use Wekinator to analyze captured data, and we'll map that data to some useful output. 21 | 22 | ### Target Audience / Prerequisite & Pre-Assessment 23 | This module is part of DM-GY 6063, _Programming is the Art of the Possible_. This is a second semester creative coding course, designed for students who have a strong JavaScript foundation. 24 | 25 | ### Outcomes & Goals 26 | - In this class we will use webcams, depth cameras, and motion trackers to capture gesture data. 27 | - We will analyze that data and map it to some other process. 28 | - Students will walk away with a deeper understanding of the technologies available for gestural analysis, as well as the challenges inherit in using that data. 29 | 30 | ### Pacing / Duration 31 | TBD 32 | 33 | ## Materials Needed 34 | To be decided, but as a point of departure: 35 | - Hardware 36 | - Webcam 37 | - Depth Camera (Kinect) 38 | - Trackpad 39 | - Leap Motion 40 | - Drawing pad + pen 41 | - Bela.io 42 | - Software 43 | - Max/MSP 44 | - Processing 45 | - Wekinator 46 | 47 | ### Exercises To Do Before Class 48 | TBD, maybe there can be some reading on statistical gestural analysis? HMMs? 49 | 50 | ### Vocabulary 51 | * Gesture - Semiotic dynamic posture. Reconfiguration of a body, relative to its own reference frame or to the world around it, in a way that deliberately or incidentally conveys meaning. (provisional definition to be revisited). 52 | * Features - Parameters of a model that best-explains a given data set. Please revisit this definiton as it's garbage. 53 | 54 | ## Exercise Descriptions 55 | TBD 56 | 57 | ## Student Reflections, Takeaways & Next Steps 58 | TBD 59 | 60 | ## Post Session 61 | TBD 62 | 63 | ### References 64 | TBD 65 | 66 | ### Implementation Guidance & Teaching Reflection 67 | TBD 68 | 69 | ***With thanks and acknowledgement, this is based on the template provided by [Eyebeam](https://github.com/eyebeam/curriculum/blob/master/TEMPLATE.md)*** -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js 2 | node_modules -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 Jenn Schiffer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/README.md: -------------------------------------------------------------------------------- 1 | Starter React App on Glitch 2 | =========================== 3 | 4 | This app is a very small scaffold to get you started using React and Webpack. 5 | 6 | It's been copied by @starakaj for your enjoyment. You can find the original at https://glitch.com/~starter-react. 7 | 8 | This project relates to video 2 of 5 in the [React Starter Kit](https://glitch.com/react-starter-kit) video series. 9 | -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/app/app.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const ReactDOM = require("react-dom"); 3 | 4 | /* Import Components */ 5 | const RootComponent = require("./components/RootComponent"); 6 | 7 | ReactDOM.render(, document.getElementById("main")); -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/app/components/ClockFace.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | 3 | function ClockFace(props) { 4 | 5 | const [date, setDate] = React.useState(new Date()); 6 | 7 | // This will be called whenever the component renders, but because we pass an empty 8 | // array as the second argument, it will only be called once, when the component 9 | // first renders. 10 | React.useEffect(() => { 11 | 12 | const timerId = setInterval(() => { 13 | setDate(new Date); 14 | }, 1000); 15 | 16 | // By returning a function from useEffect, we tell React that we'd like this 17 | // function called when the component is unmounted 18 | return () => { clearInterval(timerId) }; 19 | 20 | }, []); 21 | 22 | let prefix = ""; 23 | let postfix = "" 24 | if (props.language === "en") { 25 | prefix = "It is"; 26 | postfix = "o'clock"; 27 | } else if (props.language === "fr") { 28 | prefix = "Il est"; 29 | postfix = "heures"; 30 | } 31 | 32 | return ( 33 |

    {prefix} {date.getHours()}:{date.getMinutes()}:{date.getSeconds()} {postfix}

    34 | ); 35 | } 36 | 37 | module.exports = ClockFace; -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/app/components/DumbTwitter.jsx: -------------------------------------------------------------------------------- 1 | // DumbTwitter.jsx 2 | const { useState, useEffect } = require('react'); 3 | const React = require('react'); 4 | const DumbTwitterForm = require("./DumbTwitterForm"); 5 | const DumbTwitterList = require("./DumbTwitterList"); 6 | 7 | /* the main page for the index route of this app */ 8 | const DumbTwitter = function() { 9 | 10 | const [tweets, setTweets] = useState([]); 11 | 12 | async function fetchTweets() { 13 | const rawData = await fetch("api/tweets"); 14 | const body = await rawData.json(); 15 | setTweets(body); 16 | } 17 | 18 | useEffect(() => { 19 | fetchTweets(); 20 | }, []); 21 | 22 | return ( 23 |
    24 |

    Dumb Twitter

    25 | 26 | 27 |
    28 | ); 29 | } 30 | 31 | module.exports = DumbTwitter; 32 | -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/app/components/DumbTwitterForm.jsx: -------------------------------------------------------------------------------- 1 | // DumbTwitterForm.jsx 2 | const { useState } = require('react'); 3 | const React = require('react'); 4 | 5 | const DumbTwitterForm = function(props) { 6 | 7 | const [user, setUser] = useState(null); 8 | const [message, setMessage] = useState(null); 9 | 10 | const updateUser = (event) =>{ 11 | setUser(event.target.value); 12 | }; 13 | 14 | const updateMessage = (event) => { 15 | setMessage(event.target.value); 16 | }; 17 | 18 | const doAsyncSubmit = async () => { 19 | const response = await fetch('api/tweet', { 20 | method: 'POST', 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | }, 24 | body: JSON.stringify(({user, message})), 25 | }); 26 | if (response.status === 200) { 27 | setUser(""); 28 | setMessage(""); 29 | if (props.onTweeted) props.onTweeted(); 30 | } 31 | }; 32 | 33 | const handleSubmit = (event) => { 34 | doAsyncSubmit(); 35 | event.preventDefault(); 36 | } 37 | 38 | return ( 39 |
    40 | 44 | 48 | 49 |
    50 | ); 51 | } 52 | 53 | module.exports = DumbTwitterForm; 54 | -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/app/components/DumbTwitterList.jsx: -------------------------------------------------------------------------------- 1 | const { useState, useEffect } = require('react'); 2 | const React = require('react'); 3 | 4 | const DumbTwitterList = function(props) { 5 | 6 | const listElements = props.tweets.map((tweet, idx) => { 7 | return

    {`${tweet.user}: ${tweet.message}`}

    ; 8 | }); 9 | 10 | return ( 11 |
    12 | {listElements} 13 |
    14 | ); 15 | } 16 | 17 | module.exports = DumbTwitterList; 18 | -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/app/components/RootComponent.jsx: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const DumbTwitter = require("./DumbTwitter"); 3 | 4 | /* the main page for the index route of this app */ 5 | const RootComponent = function() { 6 | return ( 7 | 8 | ); 9 | } 10 | 11 | module.exports = RootComponent; -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Starter React 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
    14 | 15 |
    16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "back4app-tester", 3 | "version": "0.0.1", 4 | "description": "A small scaffold to get you started using React with Webpack", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack && node server.js", 9 | "watch": "NODE_ENV=development node server.js" 10 | }, 11 | "dependencies": { 12 | "body-parser": "^1.19.0", 13 | "express": "^4.16.4", 14 | "jsx-loader": "^0.13.2", 15 | "parse": "^2.19.0", 16 | "react": "^16.8.6", 17 | "react-dom": "^16.8.6", 18 | "webpack": "^5.14.0", 19 | "webpack-cli": "^3.3.0", 20 | "webpack-dev-middleware": "^3.7.2", 21 | "webpack-dev-server": "^3.11.2" 22 | }, 23 | "engines": { 24 | "node": "12.x" 25 | }, 26 | "repository": { 27 | "url": "https://glitch.com/edit/#!/starter-react" 28 | }, 29 | "license": "MIT", 30 | "keywords": [ 31 | "node", 32 | "glitch", 33 | "express" 34 | ], 35 | "author": "" 36 | } 37 | -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/public/bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | object-assign 3 | (c) Sindre Sorhus 4 | @license MIT 5 | */ 6 | 7 | /** @license React v0.18.0 8 | * scheduler.production.min.js 9 | * 10 | * Copyright (c) Facebook, Inc. and its affiliates. 11 | * 12 | * This source code is licensed under the MIT license found in the 13 | * LICENSE file in the root directory of this source tree. 14 | */ 15 | 16 | /** @license React v16.12.0 17 | * react-dom.production.min.js 18 | * 19 | * Copyright (c) Facebook, Inc. and its affiliates. 20 | * 21 | * This source code is licensed under the MIT license found in the 22 | * LICENSE file in the root directory of this source tree. 23 | */ 24 | 25 | /** @license React v16.12.0 26 | * react.production.min.js 27 | * 28 | * Copyright (c) Facebook, Inc. and its affiliates. 29 | * 30 | * This source code is licensed under the MIT license found in the 31 | * LICENSE file in the root directory of this source tree. 32 | */ 33 | -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Futura; 3 | } -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | 3 | // init project 4 | const express = require('express'); 5 | const app = express(); 6 | const port = process.env.PORT || 3000; 7 | 8 | // Add the body parser middleware 9 | const bodyParser = require('body-parser'); 10 | app.use(bodyParser.json()); 11 | 12 | // Initialize your connection to Back4App 13 | const Parse = require('parse/node'); 14 | const { json } = require('body-parser'); 15 | 16 | Parse.serverURL = 'https://parseapi.back4app.com'; // This is your Server URL 17 | Parse.initialize( 18 | 'woTcOnHZSKrWRhQucRxAkrs4fPZWFmxBdnOH4yFS', // This is your Application ID 19 | 'TPkEzQBaiTVWdpBXqAK6jHLeaUjvvwsPcOybmehO', // This is your Javascript key 20 | 'cbdbjPOsdj7S8cNsmnFTOyYgyJrksQhVu3KjuRHm' // This is your Master key (never use it in the frontend) 21 | ); 22 | 23 | // Special piece for running with webpack dev server 24 | if (process.env.NODE_ENV === "development") { 25 | const webpack = require('webpack'); 26 | const webpackDevMiddleware = require('webpack-dev-middleware'); 27 | const config = require('./webpack.dev.config.js'); 28 | const compiler = webpack(config); 29 | 30 | // Tell express to use the webpack-dev-middleware and use the webpack.config.js 31 | // configuration file as a base. 32 | app.use(webpackDevMiddleware(compiler, { 33 | publicPath: config.output.publicPath, 34 | })); 35 | } 36 | 37 | // http://expressjs.com/en/starter/static-files.html 38 | app.use(express.static('public')); 39 | 40 | // http://expressjs.com/en/starter/basic-routing.html 41 | app.get("/", function(request, response) { 42 | response.sendFile(__dirname + '/app/index.html'); 43 | }); 44 | 45 | const dummyData = [ 46 | {user: "Tom", message: "Hi I'm Tom"}, 47 | {user: "Alex", message: "Hi I'm Alex"}, 48 | {user: "Tom", message: "Hi Alex, nice to meet you"} 49 | ]; 50 | 51 | // Fetch tweets from the database. For now, just fetch the first 100 52 | app.get("/api/tweets", (_, res, next) => { 53 | let tweetClass = Parse.Object.extend("tweet"); 54 | let query = new Parse.Query(tweetClass); 55 | 56 | // Sort by their creation date 57 | query.descending("createdAt"); 58 | 59 | query.find().then(results => { 60 | res.json(results); 61 | }).catch(err => { 62 | next(err); 63 | }); 64 | }); 65 | 66 | // Post a new tweet 67 | app.post("/api/tweet", (req, res) => { 68 | const body = req.body; 69 | const user = body.user; 70 | const message = body.message; 71 | if (!user || !message) { 72 | res.status(400).send("Missing user or message"); 73 | } else { 74 | 75 | let tweetClass = Parse.Object.extend("tweet"); 76 | let tweet = new tweetClass(); 77 | tweet.set("user", user); 78 | tweet.set("message", message); 79 | 80 | tweet.save().then(result => { 81 | res.json(result); 82 | }).catch(err => { 83 | next(err); 84 | }); 85 | } 86 | }); 87 | 88 | // listen for requests 89 | const listener = app.listen(port, function () { 90 | console.log('Your app is listening on port ' + port); 91 | }); 92 | -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.jsx', 7 | output: { 8 | path: path.join(__dirname, 'public'), 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.jsx'], 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?$/, 18 | loader: 'jsx-loader', 19 | exclude: /node_modules/, 20 | include: path.join(__dirname, 'app'), 21 | }, 22 | ], 23 | }, 24 | }; -------------------------------------------------------------------------------- /lessons/14-back4app-experiment/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'development', 5 | context: path.join(__dirname, './'), 6 | entry: './app/app.jsx', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.join(__dirname, 'public'), 10 | filename: 'bundle.js', 11 | publicPath: '/', 12 | }, 13 | resolve: { 14 | extensions: ['.js', '.jsx'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.jsx?$/, 20 | loader: 'jsx-loader', 21 | exclude: /node_modules/, 22 | include: path.join(__dirname, 'app'), 23 | }, 24 | ], 25 | }, 26 | devServer: { 27 | contentBase: path.join(__dirname, 'public'), 28 | port: 3000 29 | } 30 | }; -------------------------------------------------------------------------------- /lessons/TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # About Our Template 2 | 3 | Eyebeam's educational focus is to promote artist-led, STEAM based education. This template is intended for our teaching artists to document their lessons with a larger goal for their curriculum to be referenced or used more easily in classrooms, libraries, after school programs or anywhere STEAM programming can be offered. 4 | 5 | This template is based off of NYC Department of Education's CS4All Blueprint to teach creative computing. For more information on the core components they advise teachers and classes focus on, and to give the students the competency to be better prepared to engage with more difficult STEM lessons, I highly encourage you to read about [their approach here](https://blueprint.cs4all.nyc/what-is-cs/). 6 | 7 | # TEMPLATE BELOW 8 | 9 | ## Class Title 10 | 11 | ## Authors 12 | Your name (please include collaborators or funding institutions that have supported this work) and links to your site or Github. 13 | 14 | ## Essential Questions 15 | Questions that lead to meaningful exploration of CS concepts and practices. Examples: 16 | - How can programming represent your ideas and beliefs? 17 | - How might we use math to express ourselves creatively? 18 | - How might we use computing to impact our community? 19 | - What information is my computer sharing about me or my online activity? 20 | 21 | ## Introduction 22 | Please provide a narrative of what the unit is about, and why we should learn it that is simple enough that a student could read and understand. Example: "In this workship we will be using ... to explore ... so that you have a better understanding of how ..." 23 | 24 | ### Target Audience / Prerequisite & Pre-Assessment 25 | What age range is this exercise designed for and what do students and teachers need to know or be able to do to be successful in the workshop? Any coding languages they should already be comfortable with, any frameworks or tools they should have installed before class. 26 | 27 | ### Outcomes & Goals 28 | This can be easily answered by completing these example sentences: 29 | * In this workshop we will be… (soldering, setting up a RPi home network, making a wearable that communicates with….) 30 | * Students will walk away with a deeper understanding of… 31 | 32 | ### Pacing / Duration 33 | Number of total hours the unit will take in a typical workshop session(s). Please try to take into account transition time between instruction and hands on exercises if any prep is necessary. 34 | 35 | - Break down of the class schedule example: 36 | - :15 Overview, context, examples and vocabulary 37 | - :20 Instruction & hands on exercise 38 | - :15 Wrap-up discussion & sharing, reflection or journal and next steps 39 | 40 | ## Materials Needed 41 | What hardware, software, or other materials will students or teachers need for lessons. 42 | 43 | ### Exercises To Do Before Class 44 | What materials (readings, tasks, exercises) should students complete before class to be prepared for the lesson. 45 | 46 | ### Vocabulary (example) 47 | * Program: A procedure, or set of instructions, that performs a specific task when executed by a computer. 48 | * Programming Language: The human-readable commands and syntax (or grammar rules) used to write programs. 49 | 50 | ## Exercise Descriptions 51 | Descriptions of each exercise or phase of class. Similar to pacing but with more description of steps. 52 | 53 | ## Student Reflections, Takeaways & Next Steps 54 | Additional materials for the students to leave with that can help them dig deeper into the subject or additional exercises and challenges to help students progress their knowledge to the next level and gain mastery of the subject through independent study. 55 | 56 | * Multiple Project Exit Points: an idea of high-medium-low projects so students are locked into one end product. 57 | * First Steps - a simple exercise 58 | * Next Steps - medium exercise 59 | * Big Steps - a challenge or open ended study 60 | * Presentation: how might students share their work? With peers, outside world? What media or platforms could/should be referenced to students to encourage sharing (Instagram, Tumblr...)? 61 | * Reflection: reflection questions that ask students to think about CS concepts and practices. How can students express what they’ve learned in some creative way? 62 | 63 | ## Post Session 64 | 65 | ### References 66 | Include any sources cited, but not directly linked in the unit. 67 | 68 | ### Implementation Guidance & Teaching Reflection 69 | e.g. Please provide some guidance based on experience delivering the unit and potential modifications might you are considering making for future iterations of this unit. This is an opportunity for you as the unit author to give teachers practical guidance. 70 | 71 | ***With thanks and acknowledgement, this is based on the template provided by [Eyebeam](https://github.com/eyebeam/curriculum/blob/master/TEMPLATE.md)*** -------------------------------------------------------------------------------- /links.md: -------------------------------------------------------------------------------- 1 | - https://www.shield.ai/ 2 | - https://criticalengineering.org/ 3 | -------------------------------------------------------------------------------- /makeup/00-intro.md: -------------------------------------------------------------------------------- 1 | # Making up for 00-intro 2 | 3 | You only need to complete this assingment if you missed the first class, 00-intro. 4 | 5 | ## Questions 6 | 7 | Please write an answer to the following questions. There isn't necessarily a "correct" answer, but you should think about the question and try to provide a thoughtful response. 8 | 9 | ### 1. What makes a program good? (100 words) 10 | 11 | ### 2. Please define the following terms in your own words 12 | - Protocol: 13 | - Interface: 14 | - Encoding: 15 | 16 | ### 3. What is the difference between these two interfaces? (200 words) 17 | 18 | ![ATM](../lessons/00-intro/img/atm.jpg) 19 | ![Human bank teller](../lessons/00-intro/img/bank-teller.jpg) 20 | 21 | ## Handing it in 22 | Please email your responses to me at st2774@nyu.edu. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "programming-is-possible", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Course materials for the programming is the art of the possible class", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/starakaj/programming-is-possible.git" 13 | }, 14 | "author": "Sam Tarakajian", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/starakaj/programming-is-possible/issues" 18 | }, 19 | "homepage": "https://github.com/starakaj/programming-is-possible#readme", 20 | "workspaces": [ 21 | "lessons/*" 22 | ], 23 | "engines": { 24 | "node": "12.x", 25 | "yarn": "1.21.x" 26 | }, 27 | "dependencies": {} 28 | } 29 | -------------------------------------------------------------------------------- /reading_list.md: -------------------------------------------------------------------------------- 1 | # Reading List 2 | 3 | ## Fun Reading 4 | - http://www.media-arts-uts.com/aes1/wp-content/uploads/2012/01/BasicAnimationAesthetics.pdf 5 | - https://www.mnn.com/earth-matters/climate-weather/stories/pigeons-write-a-smog-blog 6 | - https://mutamorphosis.wordpress.com/2008/10/03/pigeonblog/ 7 | - https://github.com/fpereiro/backendlore 8 | - https://www.sametab.com/blog/frameworks-for-remote-working 9 | - Graeber, David. _The Utopia of Rules on Technology, Stupidity, and the Secret Joys of Bureaucracy._ 10 | - Shaw, Donald L. "Blue Tigers" _Borges: Ficciones._ Grant & Cutler, 1993. 11 | - https://aaronzlewis.com/blog/2021/01/17/inside-the-digital-sensorium/ "A website is not a place but a new sensorium" (NO) 12 | - Has the nice quotation about interfaces, and some fun stories as well 13 | - https://urcad.es/writing/new-american-interfaces/ "New American Interfaces" 14 | - Good questions about interfaces, doesn't have that sensorium thing I'm looking for. 15 | - https://thecreativeindependent.com/people/a-charming-conversation-between-you-a-computer-and-me/ 16 | - "design interfaces with the qualities of memorable conversations in mind" 17 | - "You should feel like your input matters and that you’re more than just a consumer being sold a product or idea" 18 | - https://thecreativeindependent.com/people/lucy-siyao-liu-on-drawing-instructions/ 19 | - Relationship between instructions and creativity, representations, relations. 20 | - "How can instructions relay various techniques and skills, while simultaneously leaving space to accommodate evolution and interpretation?" 21 | - Related drawings: https://www.are.na/the-creative-independent-1522276020/drawing-instructions 22 | - https://thecreativeindependent.com/people/toni-dove-on-technology-as-subject-matter/ 23 | - Toni Dove, large installations, interactive installations, relationship between art world and artists. 24 | - Balkanization of the art-technology space 25 | - https://www.ucl.ac.uk/bartlett/public-purpose/sites/public-purpose/files/final_why_the_ethics_of_quantification_is_needed_now_saltelli_et_al_20_jan.pdf 26 | - Ethics of quantification. Seems cool but isn't necessarily 1000% percent relevant. Could talk about it at some point of course. 27 | - https://theconvivialsociety.substack.com/p/the-insurrection-will-be-live-streamed 28 | - Take on the blend between real and digital 29 | - Interfaces and interpermeability 30 | - https://link.springer.com/article/10.1007/s00146-020-01140-6 31 | - Wow check out this well documented IoT project 32 | - Kind of fun actually. Ethical implications? 33 | 34 | ## Fun Watching 35 | - https://www.youtube.com/watch?v=-0vrrTQFCjA (esp 21:13 - 24:09) 36 | - https://www.youtube.com/watch?v=Jkupc48RBRw (Daito - Drones) 37 | - https://www.youtube.com/watch?v=2jIlLHfSEfs (Leafcutter light thing) 38 | - https://www.dwbowen.com/telepresentwater (Tele-Present Water David Bowen) 39 | - https://vimeo.com/386776425 (Invisible Hand, https://collectiveoftwo.com/) 40 | --------------------------------------------------------------------------------