diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc
index d43e07eb6a..ead8271135 100644
--- a/src/objectliterals.jsdoc
+++ b/src/objectliterals.jsdoc
@@ -429,6 +429,16 @@
* @todo stability experimental
*/
+/**
+ * @typedef {Object} ol.source.GeoJSONOptions
+ * @property {Array.
|undefined} attributions Attributions.
+ * @property {string|undefined} logo Logo.
+ * @property {GeoJSONObject|undefined} geoJSON Object.
+ * @property {ol.proj.ProjectionLike} projection Projection.
+ * @property {string|undefined} string String.
+ * @proprtty {string|undefined} url URL.
+ */
+
/**
* @typedef {Object} ol.source.MapQuestOptions
* @property {ol.TileLoadFunctionType|undefined} tileLoadFunction Optional
diff --git a/src/ol/source/geojsonsource.js b/src/ol/source/geojsonsource.js
new file mode 100644
index 0000000000..2cd8dbc7fc
--- /dev/null
+++ b/src/ol/source/geojsonsource.js
@@ -0,0 +1,39 @@
+// FIXME load from URL
+
+goog.provide('ol.source.GeoJSON');
+
+goog.require('goog.asserts');
+goog.require('ol.proj');
+goog.require('ol.reader.GeoJSON');
+goog.require('ol.source.Vector');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.Vector}
+ * @param {ol.source.GeoJSONOptions=} opt_options Options.
+ */
+ol.source.GeoJSON = function(opt_options) {
+
+ var options = goog.isDef(opt_options) ? opt_options : {};
+
+ var projection = goog.isDef(options.projection) ?
+ options.projection : ol.proj.get('EPSG:3857');
+
+ goog.base(this, {
+ attributions: options.attributions,
+ logo: options.logo,
+ projection: projection
+ });
+
+ var addFeature = goog.bind(this.addFeature, this);
+ if (goog.isDef(options.geoJSON)) {
+ ol.reader.GeoJSON.readObject(options.geoJSON, addFeature);
+ }
+ if (goog.isDef(options.string)) {
+ ol.reader.GeoJSON.readString(options.string, addFeature);
+ }
+
+};
+goog.inherits(ol.source.GeoJSON, ol.source.Vector);