Saturday, October 10, 2009

11 Soft Synths to Add to Your Rack


Synthesizers are amazing instruments. I used to love watching videos of Vangelis, Rick Wakeman, Keith Emerson and Jean Michel Jarre play their racks of synths in the 70s and 80s. Their arms would jump from keyboard to keyboard with practiced precision, their fingers hitting buttons, moving sliders and changing patches between phrases.

What I love most about synthesizers is the way you can use them to create new sounds, or emulate existing sounds. Creating a new sound can require just as much inspiration (and perspiration) as creating a new melody or groove. Subtle variations or combinations can make all the difference.
Watch the masters at work in these Youtube videos. First is Rick Wakeman performing an amazing solo on several racks of synths.
Rick Wakeman solo.
Second, you can watch Vangelis in the studio as he is recording "China". While his fingers spend most of the time pounding on his favourite CS80, he is surrounded by an incredible number of keyboards, and utilizes some of them in his playing.
Vangelis: The Making of China.
Audiotuts has run quite a few synthesizer tutorials in the last month. Times have changed. We can now have a huge stack of software synthesizers on a laptop we can carry with us anywhere, and control them with a MIDI controller keyboard like the M-Audio Axiom that has a generous helping of knobs and sliders for controlling the synthesizers' parameters. You no longer need to own a music store or have an army of roadies to have a great synth setup.
A large range of soft synths and virtual instruments are available, and most DAWs include at least one. What is your favorite? While we can hardly scratch the surface in this article, here is a handful to get us started. Please add to the list in the comments.

1. ES2

ES2 is Logic Pro's synthesizer. Featuring three oscillators, two filters, and 32-voice polyphony, it is a powerful and versatile subtractive synthesizer. Davide Di Bucchianico shows us around the synth in his tutorial How to Design Reeses and Hoovers
.

2. Malström

Reason's Malström soft synth uses "Graintable" synthesis invented by Propellerhead, making it capable of creating unusual sounds. Propellerhead explain:
"The Malström Graintable synthesizer features all imaginable filtering and modulation options, and a couple of unimaginable ones too; Try some real-time waveform stretching, some spectral modulation, or some awesome wavetable sweeping.
"Malström comes with a wide range of meaty and exotic Graintables, letting you create anything from lush pads to scary squeals, from the pretty to the gritty. And that's just the sounds coming from Malström itself; try using this monster's audio inputs to filter other Reason devices, and let some of Malström's magic rub off on your drums or sampled vocals. With a device like this, no one can accuse your sound of being ordinary."

3. Metasynth

Metasynth by U&I Software is a very different soft synth only available for Mac OSX, effectively allowing you to "paint" sound. For more details, see Robert Pitman's tutorial An Introduction to Metasynth.

4. Prologue

Prologue is included in Cubase 5. It is a subtractive synthesizer with three oscillators, powerful multi-mode filter, four envelopes, two LFOs, a powerful modulation matrix, and on-board effects.. It comes with hundreds of presets covering a wide range of sounds.

5. Rapture LE

Rapture LE is included in Cakewalk's SONAR 8.5. It includes over 200 programs & hundreds of oscillator shapes.

6. Reaktor

Reaktor is an expensive, complex, modular soft synth (or rack of synths) by Native Instruments. It makes use of both FM and subtractive synthesis. Programming Reaktor isn't simple, but it does come with a long list of useful pre-programmed patches.

7. Subtractor

Subtractor is a subtractive synthesizer for Reason. It is an analog type polyphonic synthesizer. See it in action in Mo Volan's screencast A Basic Guide to Subtractive Synthesis (Part 1).

8. Thor

Thor is another of Propellerhead Reason's soft synths. It includes four different filter types and six forms of synthesis.

9. Vacuum

Pro Tools's Vacuum monophonic vacuum tube synthesizer is designed like an old-fashioned synth, and has all the knobs and parameters Rick Wakeman would expect. It even looks dusty. If you prefer not to program, it comes with around 200 presets.

10. Xpand!

Pro Tools's Xpand! is "a free RTAS® sample-playback/synthesis workstation plug-in that provides fast, efficient ways to access and manipulate thousands of high-quality sounds directly from within Pro Tools."
The webpage lists the following features:
  • Comprehensive sound factory for all musical styles and applications
  • More than 1,000 factory patches and 500 combinable part presets
  • Four layer-able parts for millions of combinations
  • Subtractive synthesis, wavetable, FM synthesis, sample playback, virtual tonewheels, and effects
  • 64-note polyphony
  • Quick and easy sound tweaking
  • Two built-in effect sections and four arpeggiators/phrase generators
  • Highly efficient — requires little CPU power per instance

11. ZynAddSubFX

ZynAddSubFX is a free and open source software synthesizer. It is a polyphonic, multitimbral synthesizer with three synthesis engines: additive, subtractive and "pad".

Which soft synth or virtual instrument do you prefer? How much do you use soft synths? What do you love and what do you find frustrating? Let us know in the comments.








Friday, October 9, 2009

The Search for an Awesome Candidate

Once upon a time, hiring decisions were made by an elite few at a company. But in today’s team-based corporate world, more and more people are being included in the hiring process to read resumes and interview candidates. When you get pulled into this world, it can either be a fun break from your work that results in getting a great teammate or it can be an utter nightmare that never ends…because you hired the wrong person.

The Description

The Search for an Awesome CandidateIf you get involved at the very beginning, you’ll be asked to help write the position description for your new coworker. This is not likely to be very exciting because the Human Resources folks have guidelines and boilerplate language that you can’t change. What you should focus on are specific skills you need your new coworker to have:
  • Software expertise
  • Professional certifications
  • Experience with clients/customers/animals/children/etc.
The more specific you can be, the more likely you are to find the right person.
 I once had a boss who insisted on putting exaggerated corporate language in the description to make the company and the job sound more glamorous. My team spent days convincing him that the ad would only attract overqualified, inappropriate candidates.

The Resumes

A few weeks after the position description is published, your boss will say it is time to review the resumes that they have received. There may be several people on your team reading them, with instructions to rank the resumes. Your goal is to find a person that is genuinely qualified to the do the job, and who is personally a good fit for your team. Here are some things to look for.

Length

A resume for the average 20/30-something young professional should only be one page long, two at the most. If you have a recent college graduate with no job experience yet four pages of bullet points, this is a warning sign. A real professional can say a lot with a few words. For instance: “Landed space vehicle on Earth’s moon, 1969.” Those are seven impressive words.
 I have often seen the resume with several enormous bullets and acres of white space between them under each college club and fraternity activity, stretching out over page after page. If you see this resume, toss it. They have nothing to say, but they think they can trick you into thinking they do.

Appearance

Take a minute just to glance at the resume in general. Is it organized with headings and bullets?  Can you easily find important information, like position titles, dates of employment, and employer names?
The resume is the first work product (document) you will see from your prospective office-mate. Judge it critically: organization, clarity, style, spelling, grammar. It all speaks to what sort of person the candidate is. Attentive to details, or sloppy. Thorough and clear, or lazy and confused.
 I have seen the resume written in cell phone text abbreviations, in multiple fonts and text sizes, with and without bullets in the same paragraph, and I have chuckled as I tossed it in the trash, unread. I don’t care what your Master’s is in if you can’t spell it like a grown-up.

Objective

Does the resume have an Objective? Here’s what every Objective always says, in plain English:
  • I want a job, preferably one that pays well and that I enjoy.
This is a minor waste of space, so don’t judge it too harshly. That is, unless, it’s a substantial paragraph containing all of the candidate’s hopes and dreams. Then they’re wasting your time, and confusing the personal with the professional.

Education

Professionals devote as little space as possible to their education. One line is plenty:
  • BS in Electrical Engineering, State College, 2005. 3.8 GPA, Honors.
If you encounter the resume with two paragraphs about extracurricular activities, classes, professors, and other errata, then not only have you found someone with no work experience, but also someone with no idea of what belongs on a resume. In the Internet Age, there is no excuse for this.

Work Experience

Here is the main event. Here is where you need to read most carefully and cynically. Here is where they’ve hidden the really dangerous lies. While it is fine to try to make something sound better, the danger comes from people trying to make nothing sound better.
The professional resume states real facts, succinctly:
  • Managed team of three developers.
  • Designed and maintained e-commerce Web site.
  • Wrote humorous articles for WorkAwesome.com.
The professional resume also includes real achievements:
  • Lowered costs 50% in 2008 by finding new vendors.
  • Increased Web traffic 25% in 2009 using new advertising.
  • Won Pulitzer Prize for WorkAwesome article, 2010.
The unprofessional resume uses big words to describe small ideas:
  • Liaised with departmental representatives. (Translation: Went to meetings.)
  • Conceived and executed novel organizational schema. (Translation: Created filing system.)
  • Supported numerous collaborative events with international experts. (Translation: Fetched coffee during conference calls to Europe.)
Again, beware of candidates trying to trick you. Question everything. Take your time. Remember, the person you select will either make your work life better or worse for the next several months or years. Get this right!
But also, it’s not a crime to be young and inexperienced. We all start out that way. As long as your candidate is honest and professional about what they do and don’t know, then you should give them a fair hearing. The right candidate may just be a novice with a great attitude.

The Ranking

Time to rank those resumes. I suggest three piles: Overqualified (probably 30% of your resumes), Potentials (about 20%), and… well, the third category is the pile of resumes that should have gone straight into the trash (50%). Try to find three Potentials who you really want to interview to recommend to your boss, and pick out one Overqualified person who would be great to have, even though you’re pretty sure they wouldn’t take the job. (You never know!)

The Interviews

Companies today don’t just interview someone once. They may interview a candidate several times in a row, including a management team, a peer team, and an HR team. And depending on the circumstances, there may be second or even third interviews with a candidate before making an offer.
When interviewing a candidate, be sure to follow any rules established by HR. For example, don’t make any personal remarks (no matter how well-intentioned) about the person’s gender, ethnicity, physical handicaps, etc. With that said, your goal during the interview should be to keep the candidate talking. Ask them for stories from previous positions:
  • Can you give an example of dealing with a difficult client?
  • What’s the best environment you’ve ever worked in?
  • How have you resolved conflicts with vendors?
Quiet candidates may be more than just nervous, they might have nothing to offer…which is a problem!
Drill them on their resume to make sure they know what they claim to know. And generally watch their body language. Are they just nervous, or are they confused and terrified?
When the interview is over, check yourself for your initial reaction. Did you enjoy the conversation and wish you had more time? Or was every minute a painful, horrible slog? Your first impression is probably right on the money.
 I interviewed a young man who drove over three hours to the meeting in Washington. When he entered the room, he asked if we would reimburse him for the speeding ticket he got on the way. (It was a short interview.)

Recommendations

Odds are, none of the candidates were perfect. But you should have a clear sense of who could do the job without making the rest of the team miserable. (Ideally, they could do the job while making the team even better!)

The End of the Line

Well, someone got hired. Was it the right person? If not, where in the process do you think the team went wrong?
 I was once part of a large group interviewing candidates for an analyst position. One day, we met with a young woman and we realized that this candidate had lied on her resume about critical job experience. We returned to our offices to send emails to the team leader to advise him not to pursue hiring this candidate. However, before most us could send those emails (less than 10 minutes after the interviews!) the leader had already announced his decision to hire the candidate. That episode seriously undermined the team’s trust in the leader.
Hiring the right person for the job is like panning for gold. There is a lot of dirt to sift, but there is also fool’s gold to avoid. And the process can be lengthy and frustrating. It helps to define your goals clearly. Do you want to find the perfect person, not matter how long it takes? Or do you need to hire the best available person before a certain deadline?
Whatever your hiring goals may be, remember that you’re not just looking to fill an open position, you’re looking to make your team stronger. Do yourself a favor and find that gold!

HTML Color Codes: An Exhibit On Digital Color




The HTML Color Codes exhibition features a selection of internet based artwork that address the topic of digital color. The central question that the exhibition poses is whether or not artists working with the internet are in fact limited to a “ready-made” color palette, a premise that many artists working with film, photography, and mass produced, standardized paint sets have assumed. The rationale for this question stems from theories of perception that argue that color is a not ready-made object found in a paint set or machine, but rather it is an experience that results from a complex process of light interacting with the retina and human nervous system.
The exhibition begins and ends along a polemic. On one extreme, color is viewed exclusively in terms of its “ready-made” code, indicated by the programming language that the artist has used. In order to use color on the internet, one must adopt the standardized hexadecimal system of color values. This system involves designating a six-digit code combined of letters and numbers (such as 0000cc for a deep blue), which is then interpreted by HTML for online visualization. HTML (HyperText Markup Language) is a programming language conventionally used for coding and structuring the elements on a web page. Software applications such as Macromedia Dreamweaver or Adobe Flash automate coding so that designers or artists can manipulate the space and plug in graphics without memorizing code. The first four artists featured in the exhibition (Chris Ashley, Michael Demers, Brian Piana, and Owen Plotkin) demonstrate the some of the possibilities for hexadecimal values in color-based, visual, internet art.

Look, See by Chris Ashley

Project
Look, See is on an ongoing series of HTML drawings that Ashley begun in 2000. The drawings are made using HTML tables in the WYSIWYG editor in Dreamweaver, a software application used to create websites. The WYSIWYG editor (What Your See Is What You Get) is a tool used for editing a web content that, unlike writing source code, allows for the direct manipulation of the colors and shapes that will appear online. The HTML table is a grid with a designated number of rows and columns. After making the selections for the table, Ashley assigns a hexadecimal value to each square in the grid, giving it its color and code. A new HTML drawing is made every day. The selections for this exhibition are the set of drawings from April 2009. All the images vary to some degree. However, a general aesthetic is at work throughout---solid and mostly opaque colored squares and rectangles that create a larger square shaped image. At the same time, the trick is that there is no image. It is solely code and its execution. If a user tries to click on the “image” to save it, she will find this impossible. Look, See plays with the intrinsic grid that structures most content on online (vector based images are the exception). While still retaining reference to this constraint, Ashley also manages to make each drawing visually elegant.

How to distribute elements horizontally using CSS



In this post I want to reply to a frequently asked question that I receive from my readers about how to distribute horizontally a certain number of elements within a main container using CSS. This problem is not particularly complex and can be solved simply using the CSS selector :first-child.
Before to proceed I suggest you to download my CSS 2 Visual Cheat Sheet for a practical reference guide to CSS 2 properties that can help you understand concepts illustrated in this post. The picture below illustrates an example of horizontal distribution:













This is the HTML code you can use to define the structure of your document:
    
    
    
In order to distribute horizontally the three elements contained into the wrapper we have to use some lines of CSS code. At first sight, a practical solution could be to define a class (.section), with the properties width and margin-right set to a specific value, and apply it to each
element contained into the wrapper. The problem is the right margin of the last
element that exceeds the width of the wrapper:

This is a problem because web browsers render the page in this way:

The last
layer is moved below. The question is: How can you remove the external margin of the last element without using a different CSS class with the property margin-right sets to 0?

Solution

As I said at the beginning of this article, the simplest solution is to use the CSS selector :first-child and invert the position of the margin from right to left. :first-child allow you to get an element that is the first child of another element. In this way the selector allows you to remove the left margin easily.

The first step is to define the wrapper using the following CSS code:
#wrapper{
    width:320px;
    height:60px;
    background:#EFEFEF;
}
The following code define the class.section to apply to each element within the main wrapper.
.section{
    border:solid 1px #999;
    float:left;
    height:58px;
    margin-left:10px;
    width:98px;
}
In this example I used fixed values for the properties width and margin-left but you can also use relative value (percentage). Now we have to remove the left margin adding the following code:
#wrapper div:first-child{margin-left:0px;}
Browsers interpret the previous line in this way: get the first
element contained into the element with ID=wrapper and set the property margin-left to 0. And this is the result:

The only advice is the following: IE 6 doesn’t support the selector :first-child. You can use conditional comments to define a specific CSS file for IE6 and add a new class (for example .section-first) with the same properties of the class .section but the property margin-left sets to 0.


8 Unique RSS Readers for Mac

8 Unique RSS Readers for Mac
Having a home delivery newspaper subscription is great. Every morning, we acquire our daily dose of news from all around the world right in front of our door step (well, most of the time). However, at the end of the year, we end up with a plethora of piles of newspapers (and it isn’t cheap!)/
With RSS, you can receive an infinite number of different news feeds right within one application. To help you choose how you view those feeds on your Mac, we’re listing 8 popular RSS Readers available for OS X.

NetNewsWire
NetNewsWire

NetNewsWire

With a native Cocoa interface similar to Mail.app, longtime Mac users will have no problem adapting to NetNewsWire. Create smart folders to categorize your feeds and tag subscriptions where you can filter which Flickr tags you’d like to see in your feeds.
Because it’s owned by NewsGator Technologies, you can sync your NewsGator account with NetNewsWire. Not only that, you can also sync it with Google Reader in NetNewsWire 3.2. Lastly, it has a pretty full built-in tab-supported browser so you won’t have to leave the app to view an associated website.
Price: Free
Developer: NewsGator Technologies
Requires: Mac OS X 10.5 or higher
Times
Times

Times

This one probably shows off the most pizzazz. Times’ interface was designed to look like a realistic newspaper — one where you get to pick the contents in each column. Perhaps the slickest feature is an animation where you “turn a page” over.
If you’re a Twitter user, you might also fall in love with it’s feature to post to Twitter directly through the app. You can import feeds via Mail, Safari, NetNewsWire or OPML but there is no built-in browser.
Price: $30
Developer: acrylic
Requires: Mac OS X 10.5 or higher
NewsLife
NewsLife

NewsLife

If you like those minimalist Mac apps that have long but narrow windows with liquid layout then NewsLife might be the RSS app for you. As you scroll along your feed subscription, NewsLife provides a brief description and a related image on the side.
Despite the lack of smart folders and further filters, there are plenty of keyboard shortcuts to use in the app which other RSS apps don’t support and you can also post an article directly to your blog.
Price: €10.00
Developer: ThinkMac
Requires: Mac OS X 10.4 or higher
NewsFire
NewsFire

NewsFire

Similar to NewsLife, NewsFire has a beautiful Cocoa interface that can also be compared to Tweetie. NewsFire has a range of customisation options e.g., displaying feeds from oldest to newest or vice versa. Using smart folders or smart feeds to organize your subscriptions is also possible.
If you have previously had an RSS account then you can import its OPML file into NewsFire. A recommended RSS app for Twitter users, since Safari140 is built in and you can post directly to Twitter with Bit.ly as the URL shortener.
Price: Free
Developer: David Watanabe
Requires: Mac OS X 10.5 or higher
PixelNews
PixelNews

PixelNews

PixelNews is one of the oldest RSS apps on the Mac seeing it supports Mac OS X 10.3! Its interface may not be the most pleasant, but it’s a pioneer in this list and deserves recognition.
One of its distinct features is the “Breaking News” where it displays all of your unread news. It has a great built-in browser so you won’t ever have to leave PixelNews to see the full story.
Price: $25/2 Week Trial available
Developer: Pixelated Software
Requires: Mac OS X 10.3 or higher
RSS Owl
RSS Owl

RSSOwl

If you constantly use different operating systems at work, home and your favorite coffee shop and you would prefer an RSS app that is compatible with Mac, Windows and Linux, RSSOwl is your perfect sidekick. This is all possible since it’s actually a Java application.
Of course, it doesn’t have a native Cocoa interface (notice the scrollpanes) so it wouldn’t be so unified with Mac. However, RSSOwl comes with a surfeit of RSS feeds, ranging from entertainment, science and technology which we very much enjoyed.
Price: Free
Developer: Benjamin Pasero
Requires: Mac OS X 10.3 or higher
Headline
Headline

Headline

It’s obvious that the Doseido interface designers created Headline with iChat’s look and feel (the green glass orbs) in mind and that’s a good thing! It’s very intuitive to use and has a lovely Leopard interface.
There is no built-in browser or smart folders. Although, you could import feeds from Safari and Mail and share articles via iChat.
Price: $19.95/Shareware
Developer: Doseido
Requires: Mac OS X 10.5.2 or higher
EventBox
EventBox

EventBox

EventBox is a relatively new entrant into the RSS application market, but does far more than just syndicating news feeds. It allows you to browse and update Twitter, access your FaceBook account, sync with Google Reader, and also supports Flickr, Reddit, Digg and more.
It’s a good option if you’re looking for an all-in-one application that does considerably more than just RSS.
Price: $15
Developer: The Cosmic Machine
Requires: Mac OS X 10.5 or higher

Conclusion

We conclude that by using RSS feeds as a replacement for traditional newspaper subscriptions, we are able to both save our money, as most RSS feeds on websites are free, and space. It’s a great way to stay on top of many websites without needing to visit them individually each day.
Do you use any other RSS apps that are not listed on the list? Please feel free to share them in the comments section below!

Getting Started with Google Wave – An Early Look

Getting Started with Google Wave – An Early Look
A lot of fuss has been made about the fact that email was originally designed 40 year ago, that it no longer works in our fast paced world, and that we need a change. It’s funny — go back just a couple hundred years ago and most people would have considered a 40 year old technology to be very modern. But the twentieth century obliterated that mentality.
And now, here we are. Yes, sometimes communication is hard. Sometimes it is scattered. And sometimes it’s easier to use a tool other than email. Now Google is doing their best to fix the problem and Google Wave is their attempt to do just that. But can it change the way we communicate online?
Only time will tell, but for now we can look at the application itself and see what it’s made of.

What is Google Wave?

Well, let’s start by taking a look at what Wave is:
  • It is (like) an email client.
  • It can be used as a wiki.
  • It is an instant messaging client.
  • It can be used as a file sharing tool.
  • It’s a web application.
  • It’s open — wide open.
If Wave is going to change the ‘game’, then it’s likely that the last bullet point will make the difference. Like Twitter, if Wave gains traction, developers from around the globe will hook into the service and the resulting ecosystem has the potential for diversity. But, as I mentioned above, only time will tell if that happens.
There were many who claimed Twitter wouldn’t go anywhere and we’ve seen how that turned out. But for every Twitter there are a dozen similar services laying facedown in a ditch somewhere in Silicon Valley.
In the world of web apps, ”If you build it, they will come” simply isn’t always how things work out.
But I digress. Okay, here’s what Wave is not:
  • It isn’t Twitter — yet.
  • It isn’t your blog (unless your happy with the Blogger look).
  • And despite reports to the contrary, it won’t make your coffee in the morning. Sad, I know.
The truth is, we don’t yet know just what Wave will end up replacing, if anything.

Terminology

Some of the jargon can be confusing.
Some of the jargon can be confusing.
A topic that also needs to be discussed is the naming conventions used. It can become confusing to discuss the application with all the different terms that are thrown around. Some of the concepts in Wave are similar to Gmail or other email clients, but some are not.
Here’s a short list to add some clarity:
  • Wave – the application itself (uppercase).
  • wave – a conversation, chat, message, gallery, map … whatever you put in to Wave to share with others is in a wave (lowercase).
  • Blips – separate entries in a wave.
  • Panels – the main compenents of the application.
  • Tags – just like they sound, but are functionally the same as Gmail labels.
  • Invite – actually, Wave calls these nominations as well and they are trickling out at this point. Patience is required if you’re waiting.

Getting Started

Once you’ve been blessed with an invite and log in, it can be a bit confusing what to do next. Personally, when I try a new app or service for the first time, the very first thing I do is check out the settings or preferences. In Wave, there is no Settings option in the upper right hand corner like every other Google service out there.
All the Settings are contained in a wave.
All the Settings are contained in a wave, not in the usual location.
And that’s when I realized that Wave is really quite different — it is a whole new approach for Google.
There is good news — once you get your invite, Google fills your Wave inbox with a few waves that will help you get acclimated with the application. The best way to familiarize yourself with the app is to read through (and watch) all of the intro waves. Overall, I think Google did a good job to get people started with the application, supplying waves that illustrate how the app works.
One of those waves is the Settings wave. It’s labelled ”Under Construction” at this point, but you get the idea where the Wave team was going with this. All content within Wave is contained in a wave, whether it’s your settings, your extensions or all your communication with others.
The only items not in a wave are your contacts. And on that subject, one other thing to do immediately is use your invites. You’ll want to make sure that Contacts panel isn’t empty. Since there are only so many invites issued right now, it can be hard to try the application out when you have no one to talk to.
And once you’ve invited some colleagues, it takes a while for the invites to get to them.

The Interface

The interface is broken down into 4 main panels – Navigation, Contacts, Search and the Wave Panel.
The four components of Wave.
The four components of Wave.
Each of these can be minimized to the top of the screen, giving you a completely blank canvas for a new wave.
A clean canvas to work with.
A clean canvas to work with.
Once you have your various panels minimized, you have two options: 1) you can maximize them once again to the default position as seen in the first image above, or 2) you can click the arrow to temporarily drop them down over the window canvas (and whatever panel you may have maximized).
Keep your panels at the top while still viewing their contents.
Keep your panels at the top while still viewing their contents.
The entire approach they’ve taken is unique and took a little getting used to. But you can see how it would be helpful to focus in on a ‘conversation’ or a ‘document’.

Navigation Panel

Similar to Gmail.
Similar to Gmail.
The panel that gives most of the functionality is the Navigation panel. With it you can open and close the Search panel and control what the search panel is displaying. Like Gmail, the sidebar navigation items are really just searches and everything is labelled or tagged. That brings us to your organizational hierarchy.
Again, like Gmail, you can tag or label all of your waves with multiple values. You can then search for your tags and save your searches. This is exactly how labels work in Gmail, so Gmail users should have no trouble adjusting here.
However, unlike most other Google apps, Wave also allows you to create folders. You can even create sub-folders. As well,you can add colors to a folder, same as a saved search.
But they do differ in how they are displayed in the Search panel — waves displayed in a saved search will display the saved search name and color while waves moved to a folder will not.
The addition of folders, along with tags, allows you to structure Wave is a manner that suits you. Many people still prefer the folder approach over tagging and searching, so it’s nice to see that Wave offers you both options.

Search Panel

After using the application for a time, it becomes clear that the intended usage of this application is to extensively work from the search panel. Ever since Google expanded beyond merely a web search engine, this is how they have built their various services, so this shouldn’t surprise anyone.
And once you get in the habit, searching is a fast and easy way to get what you’re looking for.

Wave Panel

And finally, we have the wave panel, where you’ll do whatever it is you’ll do in the application. This is where you’ll carry on conversations, share documents, take meeting notes, create image galleries … you get the point.
Several aspects of the Wave panel are also unique as they move away from traditional UI approaches. Or perhaps expand on. The Playback feature is interesting, and from my usage, will be helpful when joining a larger wave later in the timeline.
Another piece of UI that caught my attention was the scrollbar. It can be clicked and dragged like any other scroll bar in an application. Or you can simply click on the arrows at either end to move a specified length — somewhat like a page up or down.
There are a few items that take some getting used to.
There are a few items that take some getting used to.
The last item worth mentioning is the wave tool bar. It contains all the items you can perform on a wave, such as moving waves to a folder, deleting a wave, replying to the wave, and marking as read or unread. Oddly enough, the options to tag a wave or attach a file are at the bottom of the Wave panel.
Overall, the Wave panel is similar to Gmail’s compose interface or the toolbar of Google Docs. If you’re familiar with Google’s applications, it will not take you long to get comfortable with Wave.

First Impressions

Overall, Wave seems like a perfectly good tool. The speed was fine and for the most part, my experience has been bug free. Keep in mind that for now, there are very few people using the application, but if anyone seems to get scaling right, it’s Google.
At this point, support for Safari seems behind Firefox and Chrome. There’s not a big difference, but I was not able to drag and drop waves into folders. Firefox, no problem. These are negligible issues that will most likely be addressed quickly.
Apart from that, there were some other signs that show this is a preview, not an app ready for public consumption. For example, the help documentation is not completely ready. Clicking one of the links in the help menu brought me here:
Still working out the kinks.
Some of the help is … less than helpful.
Google is still working out the kinks, but with a fairly stable first release, the overall performance of the application shows it has promise. The only question that remains is whether it will match the hype.

Where Now?

If you’ve been blessed with an invite — and have actually received it — then invite some more people and spend some time getting used to it. For now, reserve judgement about the value of the app — it would be premature to render at this point. It’s going to take time and use to discover how good this software is.
It makes sense to me that Google has taken this on — creating a central hub for all communication and making sure the data required sits on Google servers fits right in with their track record. I’m sure there are some privacy advocates out there right now who are shaking their heads and wondering why so many people drink from the Google fountain of Kool-Aid.
But if the application can do what is was built to do — improve communication — then the users will come in droves, regardless of any privacy concerns.

More Invites

And another congratulations is in order for @travesse, who was the recipient of yesterday’s invite. I’ll be giving out another one later today — leave a comment below and mention what you think of the privacy aspects of Google Wave for a chance to win. And thanks to everyone for reading this site — your support means a lot.

Creating a Reusable Flash Uploader with ActionScript 3.0 and PHP

In this tutorial I'll show you how to build a Flash uploader for uploading files from the user's computer. The result will be ideal for large file sizes as it will display a progress bar and the percentage left to upload. We'll even show the user the number of bytes uploaded per second.



PG

Author: Bratu Sebastian

I am a 22 year old web developer and musician from Romania. I love php,jquery,flash,as3 and lose 80% of my nights with them. I also have a lot of business ideas that I never get to build. I like to build lots of websites.
Folder icon by Michael Ludwig.
Editor's note: I'm afraid there's no demo for this tut - you'll have to download the source.zip and play around with uploading files to your own server :)

Step 1: Button Design

Create a new ActionScript 3.0 Flash file and set the dimensions to 500 x 100 pixels. We'll begin by creating a select button. Draw a rounded rectangle, I've made mine with a 5px round corner, a blue gradient and a 2px gray stroke.
Press F8 to turn it into a button and give it the instance name "select". Name the button on the stage "select_btn".
Inside the button, fill the 3 states with a slightly different gradient. Create another layer above the first and add a static text field with the text "SELECT FILE". It's best to separate the assets because it's easier to edit them later.

Step 2: Progress Bar Design

Back to the main stage, create another rounded box with a white colour and a gray stroke. This will be the progress bar. I did mine like this:
Again, press F8 and turn it into a movieclip with the name "progress". Inside the progress movieclip, create 2 more layers above the first and move the white fill into the third, leaving only the stroke on the first layer.. Rename the first layer "margin", the second "bar" and the third "mask". We're going to turn the third layer into a mask.
On the "bar" layer, create a blue rectange with the same dimensions as the fill layer, but be carefull to make it slightly bigger so that when we apply the mask we don't have holes. Place it at 0,0 and turn it into a movieclip with the instance name "bar". This will be the bar that will show the progress.
Right-click the third layer and select "Mask" from the menu. You should have something like this:

Step 3: Duplicate Button

In the library, right-click on the select button we created earlier and select "Duplicate". Name the duplicate "cancel" and in the button change the textfield's text to "CANCEL". We're making a duplicate of the select button that will be the cancel button. We'll later show only one of them in the stage.
In the main scene, create another layer above the first and place the newly created cancel button exacly at the same position as the select button.

Step 4: Dynamic Label

We're almost done with the assets. Create another text field, this time a dynamic text field and give it the instance name "label_txt". This will show the user the success message, the error message, or the progress percent. Make sure the text is not selectable.
Test the file, to see how it's going.

Step 5: Document Class

We have only one more thing to do before we begin coding; set the document class to "Uploader".

Step 6: The cCoding

In the same folder as the Flash file, create a new ActionScript file with the name "Uploader.as". The name is important for the class to be found. Begin coding the default package and import the required classes. I've used "import Flash.display.*" for speed, but once we're done, we can include only the required classes to make the file smaller.
  1. package {  
  2.       
  3.     import Flash.display.*;  
  4.     import Flash.events.*;  
  5.     import Flash.text.*;  
  6.       
  7.     import Flash.net.FileReference;  
  8.     import Flash.net.FileReferenceList;  
  9.     import Flash.net.FileFilter;  
  10.     import Flash.net.URLRequest;  
  11.     import Flash.utils.Timer;  
  12.     import Flash.events.TimerEvent;  
  13.       
  14.       
  15.     public class Uploader extends MovieClip {  
  16.           
  17.     }     
  18. }  

Step 7: Variables

We'll begin by setting a few variables:
  1. public class Uploader extends MovieClip {  
  2.           
  3.         var file:FileReference;  
  4.         var filefilters:Array;  
  5.         var req:URLRequest;  
  6.         var tm:Timer;  
  7.         var speed:Number = 0;  
  8.         var currbytes:Number = 0;  
  9.         var lastbytes:Number = 0;  
  10.           
  11.           
  12.           
  13.     }  

Step 8: Constructor Function

Create the constructor function and add the following:
  1. public function Uploader(){  
  2.     req = new URLRequest();  
  3.     req.url = ( stage.loaderInfo.parameters.f )? stage.loaderInfo.parameters.f : 'http://www.cbesslabs.com'; //'http://cbess.ro/templates/Flashtuts/Flash_uploader/upload.php';  
  4.     file = new FileReference();  
  5.     setup( file );  
  6.     select_btn.addEventListener( MouseEvent.CLICK, browse );  
  7.     progress_mc.bar.scaleX = 0;  
  8.     cancel_btn.addEventListener( MouseEvent.CLICK, cancelUpload );  
  9.     cancel_btn.visible = false;  
  10. }  
Let me explain what's going on here:
We're creating a new URLRequest class and set the url to the upload php file.
The line "( stage.loaderInfo.parameters.f )? stage.loaderInfo.parameters.f : 'http://www.google.com'" is a conditional, meaning that if we provide the movie the parameter f, it will set the url to the f parameter. Otherwise it will use the string hardcoded here, good ol' Google, for testing only.
We're doing the conditional so we can reuse the file. This way, we can change only the f parameter with a path to the url and it will upload to the specified url.
Next we're creating a new FileReference Object, the class handling the upload process. We're passing the FileReference Object to the function setup() which we'll later code to set up the various listeners.
Finally, we add click listeners to the select and cancel buttons, set the scale of the progressbar to 0 and hide the cancel button.

Step 9: Events

We're now creating the setup() function.
  1. private function setup( file:FileReference ){  
  2.     file.addEventListener( Event.CANCEL, cancel_func );  
  3.     file.addEventListener( Event.COMPLETE, complete_func );  
  4.     file.addEventListener( IOErrorEvent.IO_ERROR, io_error );  
  5.     file.addEventListener( Event.OPEN, open_func );  
  6.     file.addEventListener( ProgressEvent.PROGRESS, progress_func );  
  7.     file.addEventListener( Event.SELECT, selectHandler );  
  8.     file.addEventListener( DataEvent.UPLOAD_COMPLETE_DATA, show_message );        
  9. }  
We could omit the Event.COMPLETE and Event.CANCEL event, but I have added them just for testing. We're setting up a CANCEL event for when the user cancels the selection dialog. We have:
  • an IO_ERROR event in case the file cannot be uploaded
  • an OPEN event for when the upload begins
  • the PROGRESS event that will update the percent uploaded
  • the SELECT event for when the user has selected a file and we automatically begin uploading
  • and an UPLOAD_COMPLETE_DATA event, which is a custom event triggered when the file has been uploaded and the php file has responded to the request.
Make sure you create all the event functions otherwise you'll get an error compiling.

Step 10: Browse

We're continuing with the browse function, triggered when the select button has been clicked. We have to show the dialog box so the user can select a file:
  1. public function browse( e:MouseEvent ){  
  2.     filefilters = [ new FileFilter('Images''*.jpg') ]; // add other file filters  
  3.     file.browse( filefilters );  
  4. }  
Notice that I've added a FileFilter object inside an array and added the array to the FileReference's browse method. You can add another file type by adding another FileFilter object with a different extension. This will filter the file extensions on the select dialog so the user can only select correct file types. This is only a filename check and doesn't check whether the image file is indeed an image.

Step 11: Upload

When the user has selected a file to upload, the SELECT event is triggered. We're now calling FileReference's upload() method to upload the file to the php file on the server with the url request argument.
  1. private function selectHandler( e:Event ){  
  2.     file.upload( req );  
  3. }  

Step 12: Button Visibility

Les's create the open_func function. This function is triggered when the upload begins. We'll hide the select button and show the cancel button.
  1. private function open_func( e:Event ){  
  2.     cancel_btn.visible = true;  
  3.     select_btn.visible = false;  
  4. }  

Step 13: Progress

Create the progress function:
  1. private function progress_func( e:ProgressEvent ){  
  2.     progress_mc.bar.scaleX = e.bytesLoaded / e.bytesTotal;  
  3.     var tf = new TextFormat();  
  4.     tf.color = 0x000000;  
  5.     label_txt.defaultTextFormat = tf;  
  6.     label_txt.text = Math.round( (e.bytesLoaded/e.bytesTotal)*100)+'% uploaded';  
  7. }  
Let me explain what's going on here. We're setting the scale of the bar movieclip showing the percent uploaded. This is accomplished by dividing the bytesLoaded to bytesTotal properties of the event object. The progress event provides us with the amount of uploaded bytes and total bytes of the file.
Next, we create a TextFormat object and set the colour to black ( 0x000000 ) for the text label. We'll need this step because later we'll change the colour of the text to green or red according to the message.
Finally, we set the text field's text with the percentage uploaded.

Step 14: Error

We'll create the error function:
  1. private function io_error( e:IOErrorEvent ){  
  2.     var tf = new TextFormat();  
  3.     tf.color = 0xff0000;  
  4.     label_txt.defaultTextFormat = tf;  
  5.     label_txt.text = 'The file could not be uploaded.';  
  6.     cancel_btn.visible = false;  
  7.     select_btn.visible = true;  
  8. }  
Basically, we change the colour of the label text, set it to an error message and then swap the cancel and select buttons again.

Step 15: Show Message

Let's create the show_message function which will check whether the upload has been successful:
  1. private function show_message( e:DataEvent ){  
  2.     var tf = new TextFormat();  
  3.     if( e.data == 'ok' ){  
  4.         tf.color = 0x009900;  
  5.         label_txt.defaultTextFormat = tf;  
  6.         label_txt.text = 'The file has been uploaded.';  
  7.     } else if( e.data == 'error'){  
  8.         tf.color = 0xff0000;  
  9.         label_txt.defaultTextFormat = tf;  
  10.         label_txt.text = 'The file could not be uploaded.';  
  11.     }  
  12. }  
Here, we're testing if the data property of the UPLOAD_COMPLETE_DATA event is 'ok' or 'error' and show a message appropriately. The data property of this event contains the server response from the php script.

Step 16: Cancel

This is the last function which will be triggered when the cancel button is clicked. This calls the FileReference's cancel() function to cancel the upload. We're also calling reset() to clean up.
  1. private function cancelUpload( e:MouseEvent ){  
  2.     file.cancel();  
  3.     reset();  
  4. }  
We trigger a reset() function to clean up the assets, set the text to "" and swap the cancel and select buttons:
  1. private function reset(){  
  2.     cancel_btn.visible = false;  
  3.     select_btn.visible = true;  
  4.     label_txt.text = '';  
  5.     progress_mc.bar.scaleX = 0;  
  6. }  
Go ahead and test the file in Flash.
For now, the upload works, but at the end we get the error message. This is because we haven't provided the path parameter, so the swf takes the hardcoded google page. As that page doesn't return us 'ok' , we get the error. We have to build the php file..

Step 17: Upload Speed - Timer

Let's show the user the speed he is uploading with. In the constructor function add the lines:
  1. tm = new Timer( 1000 );  
  2. tm.addEventListener( TimerEvent.TIMER, updateSpeed );  
We're creating a timer that will run every second and will check the speed.

Step 18: Upload Speed - Method

In the open_func() function add this line:
  1. tm.start();  
This will start the timer when the upload begins. We'll now create the updateSpeed() method:
  1. private function updateSpeed( e:TimerEvent ){  
  2.     speed = Math.floor( (currbytes - lastbytes)/1024 );  
  3.     lastbytes = currbytes;  
  4. }  
This is what happens here: we're calculating the speed by subtracting the variable lastbytes from currbytes. The lastbytes variable is afterwards set to the currbytes. So, when both variables are 0, the speed is 0. The currbytes variable will hold the current number of bytes uploaded. We cannot access this directly, that's why we've created the currbytes variable. This variable will be set from our PROGRESS event where we can access the bytesLoaded property.
Lastly, we divide everything by 1024 to get the result in kilobytes and round the value for display with Math.floor().

Step 19: Final Modification

Let's add the last modification so we can go on to the php script. In the progress_func() modify the line:
  1. label_txt.text = Math.round( (e.bytesLoaded/e.bytesTotal)*100)+'% uploaded';  
with this:
  1. label_txt.text = Math.round( (e.bytesLoaded/e.bytesTotal)*100)+'% uploaded '+speed+' kb/s';  

Step 20: Full Code

Here is the full code for the Flash uploader:
  1. package {  
  2.       
  3.     import Flash.display.*;  
  4.     import Flash.events.*;  
  5.     import Flash.text.*;  
  6.       
  7.     import Flash.net.FileReference;  
  8.     import Flash.net.FileReferenceList;  
  9.     import Flash.net.FileFilter;  
  10.     import Flash.net.URLRequest;  
  11.     import Flash.utils.Timer;  
  12.     import Flash.events.TimerEvent;  
  13.       
  14.       
  15.     public class Uploader extends MovieClip {  
  16.           
  17.         var file:FileReference;  
  18.         var filefilters:Array;  
  19.         var req:URLRequest;  
  20.         var tm:Timer;  
  21.         var speed:Number = 0;  
  22.         var currbytes:Number = 0;  
  23.         var lastbytes:Number = 0;  
  24.           
  25.         public function Uploader(){  
  26.             req = new URLRequest();  
  27.             req.url = ( stage.loaderInfo.parameters.f )? stage.loaderInfo.parameters.f : 'http://www.google.com';  
  28.             file = new FileReference();  
  29.             setup( file );  
  30.             select_btn.addEventListener( MouseEvent.CLICK, browse );  
  31.             progress_mc.bar.scaleX = 0;  
  32.             tm = new Timer( 1000 );  
  33.             tm.addEventListener( TimerEvent.TIMER, updateSpeed );  
  34.             cancel_btn.addEventListener( MouseEvent.CLICK, cancelUpload );  
  35.             cancel_btn.visible = false;  
  36.         }  
  37.           
  38.         public function browse( e:MouseEvent ){  
  39.             filefilters = [ new FileFilter('Images''*.jpg') ]; // add other file filters  
  40.             file.browse( filefilters );  
  41.         }  
  42.           
  43.         private function setup( file:FileReference ){  
  44.             file.addEventListener( Event.CANCEL, cancel_func );  
  45.             file.addEventListener( Event.COMPLETE, complete_func );  
  46.             file.addEventListener( IOErrorEvent.IO_ERROR, io_error );  
  47.             file.addEventListener( Event.OPEN, open_func );  
  48.             file.addEventListener( ProgressEvent.PROGRESS, progress_func );  
  49.             file.addEventListener( Event.SELECT, selectHandler );  
  50.             file.addEventListener( DataEvent.UPLOAD_COMPLETE_DATA, show_message );        
  51.         }  
  52.           
  53.         private function cancel_func( e:Event ){  
  54.             trace( 'canceled !' );  
  55.         }  
  56.           
  57.         private function complete_func( e:Event ){  
  58.             trace( 'complete !' );  
  59.         }  
  60.           
  61.         private function io_error( e:IOErrorEvent ){  
  62.             var tf = new TextFormat();  
  63.             tf.color = 0xff0000;  
  64.             label_txt.defaultTextFormat = tf;  
  65.             label_txt.text = 'The file could not be uploaded.';  
  66.             tm.stop();  
  67.             cancel_btn.visible = false;  
  68.             select_btn.visible = true;  
  69.         }  
  70.           
  71.         private function open_func( e:Event ){  
  72.             //trace( 'opened !' );  
  73.             tm.start();  
  74.             cancel_btn.visible = true;  
  75.             select_btn.visible = false;  
  76.         }  
  77.           
  78.         private function progress_func( e:ProgressEvent ){  
  79.             progress_mc.bar.scaleX = e.bytesLoaded / e.bytesTotal;  
  80.             var tf = new TextFormat();  
  81.             tf.color = 0x000000;  
  82.             label_txt.defaultTextFormat = tf;  
  83.             label_txt.text = Math.round( (e.bytesLoaded/e.bytesTotal)*100)+'% uploaded '+speed+' kb/s';  
  84.             currbytes = e.bytesLoaded;  
  85.         }  
  86.           
  87.         private function selectHandler( e:Event ){  
  88.             file.upload( req );  
  89.               
  90.         }  
  91.           
  92.         private function show_message( e:DataEvent ){  
  93.             tm.stop();  
  94.             var tf = new TextFormat();  
  95.             if( e.data == 'ok' ){  
  96.                 tf.color = 0x009900;  
  97.                 label_txt.defaultTextFormat = tf;  
  98.                 label_txt.text = 'The file has been uploaded.';  
  99.             } else if( e.data == 'error'){  
  100.                 tf.color = 0xff0000;  
  101.                 label_txt.defaultTextFormat = tf;  
  102.                 label_txt.text = 'The file could not be uploaded.';  
  103.             }  
  104.         }  
  105.           
  106.         private function updateSpeed( e:TimerEvent ){  
  107.             speed = Math.round( (currbytes - lastbytes)/1024 );  
  108.             lastbytes = currbytes;  
  109.         }  
  110.           
  111.         private function cancelUpload( e:MouseEvent ){  
  112.             file.cancel();  
  113.             reset();  
  114.         }  
  115.           
  116.         private function reset(){  
  117.             cancel_btn.visible = false;  
  118.             select_btn.visible = true;  
  119.             label_txt.text = '';  
  120.             progress_mc.bar.scaleX = 0;  
  121.         }  
  122.           
  123.     }     
  124. }  

Step 21: The PHP Script

Let's build our php script quickly:
  1.   
  2. $uploads_dir = './uploads/';  
  3.   
  4. if$_FILES['Filedata']['error'] == 0 ){  
  5.   
  6.   if( move_uploaded_file( $_FILES['Filedata']['tmp_name'], $uploads_dir.$_FILES['Filedata']['name'] ) ){  
  7.   
  8.   echo 'ok';  
  9.   
  10.   exit();  
  11.   
  12.   }  
  13.   
  14.   }  
  15.   
  16.   echo 'error';  
  17.   
  18.   exit();  
  19.   
  20. ?>  
I'll just quickly sum this up (PHP isn't strictly within the scope of this tut). We define a path where we will put the file, then we check whether the $_FILES['Filedata']['error'] is 0 ( if there are no errors ). We then check if move_uploaded_file() has successfully transferred the file in the folder and we show "ok" or "error" depending on the result.
One last point: you'll have to make sure that the folder exists and it is writable before running the script.
This is the end of our tutorial. Thank you for reading, I hope you learned something!