Skip to content

Instantly share code, notes, and snippets.

View mariotacke's full-sized avatar
💻
Specializing in the outrageous.

Mario Tacke mariotacke

💻
Specializing in the outrageous.
View GitHub Profile
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%&()/\\+<>';
const randomCharacter = charset[Math.floor(Math.random() * charset.length)];
outputContext.fillStyle = `rgb(${r},${g},${b})`;
outputContext.fillText(randomCharacter, x, y);
for (let y = 0; y < height; y += fontHeight) {
for (let x = 0; x < width; x += fontWidth) {
const frameSection = hiddenContext.getImageData(x, y, fontWidth, fontHeight);
const { r, g, b } = getAverageRGB(frameSection);
outputContext.fillStyle = `rgb(${r},${g},${b})`;
outputContext.fillText('@', x, y);
}
}
const getAverageRGB = (frame) => {
const length = frame.data.length / 4;
let r = 0;
let g = 0;
let b = 0;
for (let i = 0; i < length; i++) {
r += frame.data[i * 4 + 0];
g += frame.data[i * 4 + 1];
const fontHeight = 12;
if (width && height) {
// canvas setup removed for brevity
hiddenContext.drawImage(video, 0, 0, width, height);
outputContext.textBaseline = 'top';
outputContext.font = `${fontHeight}px Consolas`;
const text = outputContext.measureText('@');
const video = document.querySelector('#camera-stream');
const hiddenCanvas = document.querySelector('#hidden-canvas');
const outputCanvas = document.querySelector('#output-canvas');
const hiddenContext = hiddenCanvas.getContext('2d');
const outputContext = outputCanvas.getContext('2d');
const processFrame = () => {
const { videoWidth: width, videoHeight: height } = video;
if (width && height) {
const constraints = {
video: {
width: 512,
height: 512,
},
};
navigator.getUserMedia(constraints, function (stream) {
video.srcObject = stream;
video.play();
@mariotacke
mariotacke / root-reducer.ts
Created June 22, 2019 04:28
Redux + Typescript Samples
import { combineReducers } from 'redux';
import timeReducer from './time';
const rootReducer = combineReducers({
time: timeReducer,
});
export type AppState = ReturnType<typeof rootReducer>;
@mariotacke
mariotacke / index.js
Last active May 20, 2019 02:01
blog-real-time-word-cloud
async function init () {
const cloud = await getCloud();
const highestScore = Math.max(...cloud.map((x) => x.size));
var layout = d3.layout.cloud()
.size([500, 500])
.words(cloud)
.rotate(function () { return ~~(Math.random() * 2) * 90; })
.font("Impact")
@mariotacke
mariotacke / index.js
Last active May 20, 2019 01:08
blog-real-time-word-cloud
async function getCloud () {
const response = await fetch('/api/channel/$CHANNEL_NAME/words');
const json = await response.json();
const cloud = json.scores.map(function ({ key, value }) {
return { text: key, size: value };
});
return cloud;
}
@mariotacke
mariotacke / server.js
Last active May 20, 2019 01:03
blog-real-time-word-cloud
app.get('/api/channel/:channel/words', async function (req, res) {
const channel = `#${req.params.channel}`;
const args = [channel, '0', '50', 'WITHSCORES'];
const scores = [];
const range = await redisClient.zrevrangeAsync(args);
for (let i = 0; i < range.length; i += 2) {
scores.push({
key: range[i],