”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > VR中用Three.js和Node可视化Twitter流

VR中用Three.js和Node可视化Twitter流

发布于2025-04-16
浏览:749

VR中用Three.js和Node可视化Twitter流

Twitter is a wonderful and information rich beast. I wanted to combine some of the powers of visualization, Three.js and its VR capabilities with Socket.IO and Node to create a pretty world of particles generated via a Twitter stream.

I’ve previously discussed all the basics of developing virtual reality web experiences in the SitePoint article Bringing VR to the Web with Google Cardboard and Three.js, so if you are new to this whole idea – read that one first and come back. This demo uses the same foundations.

The demo we will be building will watch a live Twitter stream for a keyword. When one is tweeted out whilst it is watching the stream, it will bring up a “tower” of shining particles that represent how long the tweet was. This demo in particular will look for mentions of the word “pizza”. Why pizza you ask? I was looking for a term which was not mentioned as frequently as “bieber” but more frequently than “boxcar racing hyenas”. In short, the best terms are ones which are relatively frequent enough that they’ll appear whilst you are watching, but not so frequent that they come through at many hundreds a second. Pizza is one of them.

Key Takeaways

  • Utilize Node.js and Socket.IO to create a real-time server that can handle and emit Twitter stream data, enhancing the interactivity of VR visualizations.
  • Integrate Three.js to construct a 3D VR environment where tweets are represented as unique particle towers, varying in height based on tweet length.
  • Optimize user experience in VR by setting a maximum particle count and arranging particle towers within a defined range to prevent performance lags.
  • Customize the visual aspects of tweet representations by using user profile colors for particles and employing textures and blending options available in Three.js.
  • Ensure broad accessibility and real-time functionality by deploying the Node server on platforms like Heroku and testing the VR experience on various devices using tunneling services like ngrok.

Demo Code

If you’re keen to get straight into the code and try it out, you can find it here on GitHub.

Want to try it in action? I’ve got a running version hosted here: VR Twitter World.

Our Server Code

We’ll begin by looking at our Node server code. It will display our flat HTML and also operate as a Socket.IO server that’ll pull in a stream of data from Twitter.

The full server is relatively short and looks like so:

var express = require('express'),
    app = express(),
    server = require('http').createServer(app),
    port = process.env.PORT || 80,
    io = require('socket.io')(server),
    config = require('./config.json'),
    Twitter = require('node-tweet-stream'),
    t = new Twitter(config);

app.get('/', function(request, response) {
  response.sendFile(__dirname   '/public/index.html');
});

app.get(/^(. )$/, function(req, res) {
  res.sendFile(__dirname   '/public/'   req.params[0]);
});

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});
 
server.listen(port, function() {
  console.log('Listening on '   port);
});

t.track('pizza');
t.on('tweet', function(tweet){
  console.log('Roger that. Tweets incoming!');
  console.log(tweet);

  io.emit('tweet', tweet);
});

t.on('error', function (err) {
  console.log('Brace yourself! We are goin doooowwwwwwnnnnnnnn! ', err);
});

Our first lines set up a server using the Node Express framework. It’s a rather simple set up that is pulling in all our dependencies and preparing the app variable for us to access our server functionality. port sets up which port we want our server to run on (process.env.PORT is a server variable some hosting set ups like Heroku will have defined).

var express = require('express'),
    app = express(),
    server = require('http').createServer(app),
    port = process.env.PORT || 80,

Then we set up the io variable whilst simultaneously starting up our Socket.IO server functionality, attaching it to the Express server we set up above:

io = require('socket.io')(server),

Setting Up Twitter Access

The config variable is a nice way of keeping the application’s Twitter authentication keys and access tokens in their own file. In order to live view the Twitter stream, we will be using an npm module called node-tweet-stream which provides all the functions we will need. We assign the object for our Twitter access and all associated functions to the t variable, passing in our config JSON to prove we’re allowed to access it.

config = require('./config.json'),
Twitter = require('node-tweet-stream'),
t = new Twitter(config),

If you don’t have any Twitter keys for access to the Twitter API, never fear! You just need to register an app with Twitter. Head to the Twitter Application Management page, log in with your Twitter credentials and then click “Create New App”.

Once you have an app, you can get your keys and access tokens by clicking the “Keys and Access Tokens” link that will appear on your app’s management page. If you can’t find it, it will be at the URL of: https://apps.twitter.com/app/0000000/keys (replacing 0000000 with your app’s ID).

Then, create a file on the same level as index.html called config.json. Within it, add the following with your own app’s values:

{
  "consumer_key": "YOURKEY",
  "consumer_secret": "YOURKEYSECRET",
  "token": "YOURTOKEN",
  "token_secret": "YOURTOKENSECRET"
}

Other Server Basics

Further along in our index.js file, we set up calls to the root of our server to load /public/index.html:

app.get('/', function(request, response) {
  response.sendFile(__dirname   '/public/index.html');
});

We also have it serve up any other static files within the public directory on our server:

app.get(/^(. )$/, function(req, res) {
  res.sendFile(__dirname   '/public/'   req.params[0]);
});

If we do have an error, we log that error in our server’s console and return a 500 error:

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

The following lines start our server running with all the settings above.

server.listen(port, function() {
  console.log('Listening on '   port);
});

Retrieving Our Live Twitter Stream

Finally, we set up our Twitter specific server functionality. We use the track() function to specify which keyword we would like to track in the ever expanding Twitter content stream.

t.track('pizza');

We then set up a callback function to run anytime the node-tweet-stream module spots a Tweet with that keyword. If it sees one, we log it in our server’s console log (this is optional, you could remove this if you’d like) and then emit that tweet out as a Socket.IO event to any connected clients.

t.on('tweet', function(tweet){
  console.log('Roger that. Tweets incoming!');
  console.log(tweet);

  io.emit('tweet', tweet);
});

If we have an error for any reason with our Twitter API, it’ll be logged to our server logs:

t.on('error', function (err) {
  console.log('Brace yourself! We are goin doooowwwwwwnnnnnnnn! ', err);
});

All of our server dependencies and details are stored within package.json as with all Node apps. If you are new to Node.js, you might want to read up a bit on what everything means: package.json.

Our Front End Code

Our front end code begins with the same set up from the Bringing VR to the Web with Google Cardboard and Three.js article – a Three.js scene that we display through a stereoscopic effect, bringing our scene into a VR view. To keep this short and sweet, I won’t cover the bits that are the same as the previous demo from that article. If you’re unsure of anything I don’t explain here, check that earlier article for info.

Setting up Socket.IO

The only new JS file we will be adding in comparison to our previous foundation is our Socket.IO JavaScript file. It’s a simple one liner:

>>

In order to access the functionality from Socket.IO, all we need is to assign that functionality to the io variable, as you’ll see a little further down in our index.html file:

socket = io(),

Preparing Our Towers

We then set up variables for our “towers” (basically our vertical sets of particles that represent a tweet). All of our towers are stored within a THREE.Object3D object called tweetTowers. This is a container object that lets us keep track of all of our towers:

// Towers
tweetTowers = new THREE.Object3D(),
particleTexture and particleMaterial are our variables that will represent how our particles will look:
particleTexture,
particleMaterial,

maxTowerCount is the maximum number of towers we want to be visible in our scene – if this is set too high, we can end up with a laggy experience. I’ve set it to 6000 as this sets the maximum particles to be around about a million. A reasonable number in my opinion!

maxTowerCount = 6000,

range is how large of an area around the viewer we want these towers to be placed. The towers will be placed at random spots in the scene, so this limits how far apart they all are placed. I’ve found it is a nicer experience with them closer to the user. If they are further away from the user, it looks like there isn’t as many (despite there being thousands upon thousands of particles!). I set it to 100:

range = 100;

Our Init Function

There isn’t too much that is new within our init() function. It mainly sets up our VR camera and controls as explained in the previous article. The new bits are at the end.

We define our particleTexture image to be a png called particle-new.png which we have within our public folder:

particleTexture = THREE.ImageUtils.loadTexture('textures/particle-new.png');

We finish the init() function by adding our tweetTowers container to our scene. With this in our scene, we don’t need to worry about adding any of our towers directly into the scene, we just add them into our tweetTowers object directly.

scene.add(tweetTowers);

Reacting to Tweets

You’ll recall that once our server finds tweets streaming through Twitter with our keyword of “pizza”, it emits an event called “tweet”. Our client side JavaScript will now watch for that event and respond:

socket.on('tweet', function(tweet) {
  // Our response
});

The response code is a call to a function called generateTower() that will add a tower to our scene representing that tweet. We pass it four values:

generateTower({
  color: parseInt('0x'  tweet.user.profile_background_color),
  startingCoords: {
    x: getRandomArbitrary(-1*range, range),
    y: 0,
    z: getRandomArbitrary(-1*range, range)
  },
  speed: 5,
  size: (tweet.text.length / 140) * 100
});
  • color is the color of our particle. We pass in the color of the user’s profile background. This lets us show different colors to represent different hungry users tweeting about pizza.
  • startingCoords is where the tower will be placed. We want these to be placed around us, so we place them between our range variable above (this should end up between -100 to 100) on the x and z axes. If we placed them randomly on y, they’d start at different levels higher and lower from the ground rather than lining up like buildings. We definitely don’t want that, so we ensure they all are placed at a y position of 0. getRandomArbitrary() is a simple random number generator between the two values.
  • speed defines how far apart our particles end up placed (or the speed at which the tower rises if they animated upwards).
  • size is how many particles high our tower will be. We average it out to a percentage, assuming a maximum Twitter length of 140 characters.

Displaying a Tower

Our generateTower() function itself begins by defining a towerGeometry variable. This is a THREE.Geometry object that will contain the positions of all of our particles within the tower. Keeping all of the points tracked within one Geometry object can help keep processing times down, as Three.js only needs to track each tower object and its points, rather than a range of independent particles. Later in the code, we will provide the geometry to a THREE.PointCloud object which can interpret those points into our particles.

function generateTower(options) {
  var towerGeometry = new THREE.Geometry();

  // The rest of our code
}

We then set up a JavaScript object called particleMovements that stores where our particles will start and finish within the tower, along with how far apart they’ll be (the values we passed in earlier):

var particleMovements = {
  start: 0,
  end: options.size,
  speed: options.speed
};

The currentCoords variable tracks the last position of a particle within the tower. We initialize it at 0,0,0. The startingCoords where the tower will be placed are parsed in from the function call earlier. If we don’t have any starting co-ordinates from the function call, we initialize them to be the same as currentCoords:

var currentCoords = {x: 0, y: 0, z: 0},
    startingCoords = options.startingCoords ? options.startingCoords : currentCoords;

We then iterate through the size of our tower to create each particle. We set the current co-ordinates for y to increase by our speed value multiplied by i. Our x and z values remain at their starting spots as we’re only moving upwards.

for (var i = 0; i   currentCoords = {
    x: startingCoords.x,
    y: startingCoords.y   particleMovements.speed * i,
    z: startingCoords.z
  };

With those co-ordinates defined for this particle, we attach that particle’s position as a vertex in our towerGeometry object:

towerGeometry.vertices.push(new THREE.Vector3(currentCoords.x, currentCoords.y, currentCoords.z));

That ensures our positioning of the particles is set correctly. Next, we define what the particles in this tower will look like within the particleMaterial variable. Our particles will be placed within a THREE.PointCloud object and thus to style them, we will use a THREE.PointCloudMaterial material:

particleMaterial = new THREE.PointCloudMaterial({
  map: particleTexture,
  color: options.color,
  blending: THREE.AdditiveBlending,
  transparent: true,
  size: 4
});
  • map defines the image we will be using for the particle, we pass in the particleTexture we defined earlier.
  • color passes in the color we want the particle to be (defaults to 0xffffff in Three.js).
  • blending sets up how the particles blend into the scene. THREE.AdditiveBlending adds the color of the texture to the one behind it.
  • transparent ensures blending can happen as it requires a level of transparency to work.
  • size is the size of our particles.

Finally, we define our tower’s point cloud within the variable of tower. We pass in our geometry containing the points we want each particle to appear on, as well as the material we defined above for each of them.

var tower = new THREE.PointCloud(towerGeometry, particleMaterial);

We add that tower to our tweetTowers collection object and then check to see how many towers are in the scene. If we have more towers than our maximum allowed, we hide the oldest one to reduce the load on the device. If you have any performance issues, chances are they’ll be a bit better if you reduce the maxTowerCount!

tweetTowers.add(tower);
if (tweetTowers.children.length > maxTowerCount) {
  tweetTowers.children[tweetTowers.children.length - maxTowerCount].visible = false;
}

Running Our Code

To run this demo locally, you’ll need Node installed and you’ll need to run the usual commands. Install all dependencies for the project:

npm install

Then run it:

node index.js

In order to test this on your smartphone, you will either need to ensure your smartphone is on the same local network and find your computer’s IP address, or use a tunnelling service like ngrok (I cover how to use ngrok in the article on Accessing Localhost From Anywhere).

You could also host the Node server somewhere. I personally used Heroku, however this is completely personal preference.

Once you’ve got the server somewhere up and running, open up Chrome for Mobile and visit it! Put on your Google Cardboard or other similar headset and you should see an experience that after half a minute or so looks like this if you look up:

VR中用Three.js和Node可视化Twitter流

Conclusion

This should have given you a good overview of using Node, Socket.IO and Three.js to create a 3D web API enabled virtual reality visualization. The demo itself could be developed further, adding more keywords, filters, making it run smoother with more particles and so on. There is plenty of potential! Feel free to get out there and try to make your own fantastic experience from this demo!

I’ve also got other demos here at SitePoint that use similar concepts but instead brings them into an augmented reality experience. If you are interested, Filtering Reality with JavaScript and Google Cardboard explores taking in the camera from your smartphone and adding filters to it, and Augmented Reality in the Browser with Awe.js explores going all the way and augmenting elements into your field of view via the ever powerful combination of Three.js and Awe.js!

If you do take on the challenge of putting together your own VR visualization from the demo in this article (or combining it with elements from the AR examples mentioned), leave a note in the comments or get in touch with me on Twitter (@thatpatrickguy), I’ll get out my headset and take a look!

Frequently Asked Questions (FAQs) about Visualizing a Twitter Stream in VR with Three.js and Node

How can I set up Twitter for websites?

Setting up Twitter for websites involves a few steps. First, you need to create a Twitter application on the Twitter Developer’s site. After creating the application, you will receive a set of keys and tokens. These are used to authenticate your application with Twitter. You then need to install the Twitter JavaScript API on your website. This API allows your website to interact with Twitter, enabling features like Tweet buttons and embedded Tweets.

What is Three.js and how does it work?

Three.js is a cross-browser JavaScript library that is used to create and display animated 3D computer graphics in a web browser. It uses WebGL to render graphics. The library provides a set of objects and methods that make it easier to create complex 3D scenes, including cameras, lights, materials, and geometries.

How can I use Three.js on Node.js?

To use Three.js on Node.js, you need to install the ‘three’ package using npm, the Node.js package manager. Once installed, you can require the ‘three’ module in your Node.js scripts. You can then use the Three.js API to create 3D graphics.

How can I visualize a Twitter stream in VR?

Visualizing a Twitter stream in VR involves several steps. First, you need to set up a Twitter stream using the Twitter API. This involves creating a Twitter application and authenticating it with your Twitter account. Once the stream is set up, you can use Three.js to create a 3D visualization of the Tweets. This involves creating a 3D scene, adding objects to represent Tweets, and updating the scene in real-time as new Tweets arrive.

What are the key differences between Three.js and other 3D graphics libraries?

Three.js is a high-level library that provides a simple API for creating 3D graphics. It abstracts away many of the complexities of working with WebGL directly, making it easier to create complex 3D scenes. Other libraries may provide more low-level access to WebGL, but require a deeper understanding of 3D graphics programming.

How can I handle errors when setting up a Twitter stream?

When setting up a Twitter stream, errors can occur for a variety of reasons, such as network issues or incorrect authentication credentials. The Twitter API provides error messages that can help you diagnose and fix these issues. It’s important to handle these errors in your code to ensure your application continues to run smoothly.

How can I optimize the performance of my Three.js application?

Optimizing a Three.js application involves a variety of techniques. These include reducing the complexity of your 3D models, optimizing your textures, and minimizing the number of draw calls. You can also use tools like the Three.js inspector to analyze the performance of your application and identify bottlenecks.

How can I customize the appearance of my Twitter stream in VR?

You can customize the appearance of your Twitter stream in VR by modifying the properties of the 3D objects that represent Tweets. This includes properties like color, texture, and size. You can also use different types of lights and cameras to change the overall look and feel of the scene.

How can I add interactivity to my Twitter stream in VR?

Adding interactivity to your Twitter stream in VR involves using event listeners to detect user actions, such as clicks or touches. You can then update the 3D scene in response to these actions. For example, you could allow users to select Tweets by clicking on them, or navigate through the scene using touch gestures.

How can I deploy my Three.js application to the web?

Deploying a Three.js application to the web involves packaging your application files and uploading them to a web server. You can use tools like Webpack to bundle your JavaScript files, and services like GitHub Pages or Netlify to host your application. Once deployed, your application will be accessible to anyone with a web browser.

最新教程 更多>
  • Python中何时用"try"而非"if"检测变量值?
    Python中何时用"try"而非"if"检测变量值?
    使用“ try“ vs.” if”来测试python 在python中的变量值,在某些情况下,您可能需要在处理之前检查变量是否具有值。在使用“如果”或“ try”构建体之间决定。“ if” constructs result = function() 如果结果: 对于结果: ...
    编程 发布于2025-07-04
  • 解决MySQL插入Emoji时出现的\\"字符串值错误\\"异常
    解决MySQL插入Emoji时出现的\\"字符串值错误\\"异常
    Resolving Incorrect String Value Exception When Inserting EmojiWhen attempting to insert a string containing emoji characters into a MySQL database us...
    编程 发布于2025-07-04
  • 为什么PYTZ最初显示出意外的时区偏移?
    为什么PYTZ最初显示出意外的时区偏移?
    与pytz 最初从pytz获得特定的偏移。例如,亚洲/hong_kong最初显示一个七个小时37分钟的偏移: 差异源利用本地化将时区分配给日期,使用了适当的时区名称和偏移量。但是,直接使用DateTime构造器分配时区不允许进行正确的调整。 example pytz.timezone(...
    编程 发布于2025-07-04
  • 如何在无序集合中为元组实现通用哈希功能?
    如何在无序集合中为元组实现通用哈希功能?
    在未订购的集合中的元素要纠正此问题,一种方法是手动为特定元组类型定义哈希函数,例如: template template template 。 struct std :: hash { size_t operator()(std :: tuple const&tuple)const {...
    编程 发布于2025-07-04
  • 如何使用FormData()处理多个文件上传?
    如何使用FormData()处理多个文件上传?
    )处理多个文件输入时,通常需要处理多个文件上传时,通常是必要的。 The fd.append("fileToUpload[]", files[x]); method can be used for this purpose, allowing you to send multi...
    编程 发布于2025-07-04
  • 如何将多种用户类型(学生,老师和管理员)重定向到Firebase应用中的各自活动?
    如何将多种用户类型(学生,老师和管理员)重定向到Firebase应用中的各自活动?
    Red: How to Redirect Multiple User Types to Respective ActivitiesUnderstanding the ProblemIn a Firebase-based voting app with three distinct user type...
    编程 发布于2025-07-04
  • 如何使用node-mysql在单个查询中执行多个SQL语句?
    如何使用node-mysql在单个查询中执行多个SQL语句?
    Multi-Statement Query Support in Node-MySQLIn Node.js, the question arises when executing multiple SQL statements in a single query using the node-mys...
    编程 发布于2025-07-04
  • Java中如何使用观察者模式实现自定义事件?
    Java中如何使用观察者模式实现自定义事件?
    在Java 中创建自定义事件的自定义事件在许多编程场景中都是无关紧要的,使组件能够基于特定的触发器相互通信。本文旨在解决以下内容:问题语句我们如何在Java中实现自定义事件以促进基于特定事件的对象之间的交互,定义了管理订阅者的类界面。以下代码片段演示了如何使用观察者模式创建自定义事件: args)...
    编程 发布于2025-07-04
  • 在C#中如何高效重复字符串字符用于缩进?
    在C#中如何高效重复字符串字符用于缩进?
    在基于项目的深度下固定字符串时,重复一个字符串以进行凹痕,很方便有效地有一种有效的方法来返回字符串重复指定的次数的字符串。使用指定的次数。 constructor 这将返回字符串“ -----”。 字符串凹痕= new String(' - ',depth); console.Wr...
    编程 发布于2025-07-04
  • 如何有效地选择熊猫数据框中的列?
    如何有效地选择熊猫数据框中的列?
    在处理数据操作任务时,在Pandas DataFrames 中选择列时,选择特定列的必要条件是必要的。在Pandas中,选择列的各种选项。选项1:使用列名 如果已知列索引,请使用ILOC函数选择它们。请注意,python索引基于零。 df1 = df.iloc [:,0:2]#使用索引0和1 c...
    编程 发布于2025-07-04
  • \“(1)vs.(;;):编译器优化是否消除了性能差异?\”
    \“(1)vs.(;;):编译器优化是否消除了性能差异?\”
    答案: 在大多数现代编译器中,while(1)和(1)和(;;)之间没有性能差异。编译器: perl: 1 输入 - > 2 2 NextState(Main 2 -E:1)V-> 3 9 Leaveloop VK/2-> A 3 toterloop(next-> 8 last-> 9 ...
    编程 发布于2025-07-04
  • 如何在Chrome中居中选择框文本?
    如何在Chrome中居中选择框文本?
    选择框的文本对齐:局部chrome-inly-ly-ly-lyly solument 您可能希望将文本中心集中在选择框中,以获取优化的原因或提高可访问性。但是,在CSS中的选择元素中手动添加一个文本 - 对属性可能无法正常工作。初始尝试 state)</option> < op...
    编程 发布于2025-07-04
  • 如何使用Depimal.parse()中的指数表示法中的数字?
    如何使用Depimal.parse()中的指数表示法中的数字?
    在尝试使用Decimal.parse(“ 1.2345e-02”中的指数符号表示法表示的字符串时,您可能会遇到错误。这是因为默认解析方法无法识别指数符号。 成功解析这样的字符串,您需要明确指定它代表浮点数。您可以使用numbersTyles.Float样式进行此操作,如下所示:[&& && && ...
    编程 发布于2025-07-04
  • 哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    在Python Matplotlib's path.contains_points FunctionMatplotlib's path.contains_points function employs a path object to represent the polygon.它...
    编程 发布于2025-07-04
  • 为什么PHP的DateTime :: Modify('+1个月')会产生意外的结果?
    为什么PHP的DateTime :: Modify('+1个月')会产生意外的结果?
    使用php dateTime修改月份:发现预期的行为在使用PHP的DateTime类时,添加或减去几个月可能并不总是会产生预期的结果。正如文档所警告的那样,“当心”这些操作的“不像看起来那样直观。 ; $ date->修改('1个月'); //前进1个月 echo $ date->...
    编程 发布于2025-07-04

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3