Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm trying to make a Twitter bot that generates a random rgb colour, creates a picture of this colour and tweets it. I've created a piece of JS that can generate and tweet a random rgb value and a piece of javascript that can generate a picture of a random colour but I'm not sure how to combine the two.

My problem is that I can't generate the PNG image without having a document. If I run the script on a server using Node.JS there isn't a document to create the canvas in. Is there any other way to create a png picture (maybe by temporarily saving it to the server) and attaching it to a tweet?

Thanks for your help!

This is the code I have for tweeting a random value:

var Twit = require('twit')
var T = new Twit({
  consumer_key:         '###', 
  consumer_secret:      '###',
  access_token:         '###',
  access_token_secret:  '###'
function tweet() {
  //Generate a random colour
  var r = Math.floor((Math.random() * 256));
  var g = Math.floor((Math.random() * 256));
  var b = Math.floor((Math.random() * 256));
  var color = "rgb("+r+","+g+","+b+")";
  // tweet that colour
  T.post('statuses/update', { status: color }); 
setTimeout(tweet, 30000);

And this is a JS script that generates a PNG file of a random colour on a web page:

var r = Math.floor((Math.random() * 256));
var g = Math.floor((Math.random() * 256));
var b = Math.floor((Math.random() * 256));
var color = "rgb("+r+","+g+","+b+")";
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
// draw box
context.beginPath();
context.moveTo(0, 00);
context.lineTo(0, 800);
context.lineTo(800, 800);
context.lineTo(800, 0);
context.closePath();
context.lineWidth = 5;
context.fillStyle = color;
context.fill();
// save canvas image as data url (png format by default)
var dataURL = canvas.toDataURL();
// set canvasImg image src to dataURL
// so it can be saved as an image
document.getElementById('canvasImg').src = dataURL;

Updated for 2020:

As pointed by @Mike'Pomax'Kamermans in the comments, NodeJS now (since v7) contains the same URL object as the browser, which can be used for putting the canvas data URI into and then saved to disk similarly to this answer: https://stackoverflow.com/a/11335500/2138943

Old answer (for Node versions < 7)

You can use an Node <canvas> implementation backed with a JavaScript DOM, such as node-canvas with jsDom. For example with canvas, your code should look something like this:

    var Canvas = require('canvas');
    var Image = Canvas.Image;
    var canvas = new Canvas(800, 800);
    var context = canvas.getContext('2d');
    var r = Math.floor((Math.random() * 256));
    var g = Math.floor((Math.random() * 256));
    var b = Math.floor((Math.random() * 256));
    var color = "rgb("+r+","+g+","+b+")";
    // draw box
    context.beginPath();
    context.moveTo(0, 00);
    context.lineTo(0, 800);
    context.lineTo(800, 800);
    context.lineTo(800, 0);
    context.closePath();
    context.lineWidth = 5;
    context.fillStyle = color;
    context.fill();
    // save canvas image as data url (png format by default)
    var dataURL = canvas.toDataURL();
    // set canvasImg image src to dataURL
    // so it can be saved as an image
    document.getElementById('canvasImg').src = dataURL;

If you want to get rid of the jsDom requirement you can use canvas.pngStream() like @josh3736 suggests in the comments (if you aren't doing anything else with the document itself ie. you only need the canvas)

Canvas#pngStream()

To create a PNGStream simply call canvas.pngStream(), and the stream will start to emit data events, finally emitting end when finished. If an exception occurs the error event is emitted.

var fs = require('fs'),
  ,  out = fs.createWriteStream(__dirname + '/text.png')   
  , stream = canvas.pngStream();
stream.on('data', function(chunk){out.write(chunk); });
stream.on('end', function(){console.log('saved png'); }); 

Currently only sync streaming is supported, however we plan on supporting async streaming as well (of course :) ). Until then the Canvas#toBuffer(callback) alternative is async utilizing eio_custom().

This is good, although since we're on the server there's really no reason to use a data URL -- just save/serve the result as a regular PNG file (canvas.pngStream()). – josh3736 Feb 1, 2016 at 19:00 Thanks @PeteTNT! I think I understand what the canvas implementation does, but I'm not sure how to use it. To be exact I'm not sure what I can pass into T.post('media/upload', { media_data: **here** }. It says the parameter is invalid when I use dataUrl. I tried var b64content = fs.readFileSync('dataUrl', { encoding: 'base64' }) but that gave a No such file-error. How can I tell the T.post where the image/canvas went? – Wouter van Dijke Feb 1, 2016 at 19:25 @josh3736 what happens when I pipe to the write stream? I'm trying to get the file to tweet like this but it doesn't work: var fs = require('fs') , out = fs.createWriteStream('/img' + r + g + b + '.png') //__dirname + , stream = canvas.pngStream(); var dataUrl = canvas.pngStream().pipe(out); var b64content = fs.readFileSync('/img' + r + g + b + '.png', { encoding: 'base64' }) // first we must post the media to Twitter T.post('media/upload', { media_data: b64content }, function (err, data, response) – Wouter van Dijke Feb 1, 2016 at 20:04 I'll leave that to you, adding in a "2020 edit: just do X" would benefit the community, but it's your answer, not mine. If I were to edit it, I should be writing a new answer instead and (if the original poster is still around) would get your answer unaccepted. – Mike 'Pomax' Kamermans Aug 5, 2020 at 0:31

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.